fix #7877 - caption handling of images
[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 Roo.htmleditor = {};
22 Roo.namespace('Roo.bootstrap.form.HtmlEditorToolbar');
23 /*
24  * Based on:
25  * Ext JS Library 1.1.1
26  * Copyright(c) 2006-2007, Ext JS, LLC.
27  *
28  * Originally Released Under LGPL - original licence link has changed is not relivant.
29  *
30  * Fork - LGPL
31  * <script type="text/javascript">
32  */
33
34
35 /**
36  * @class Roo.Shadow
37  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
38  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
39  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
40  * @constructor
41  * Create a new Shadow
42  * @param {Object} config The config object
43  */
44 Roo.Shadow = function(config){
45     Roo.apply(this, config);
46     if(typeof this.mode != "string"){
47         this.mode = this.defaultMode;
48     }
49     var o = this.offset, a = {h: 0};
50     var rad = Math.floor(this.offset/2);
51     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
52         case "drop":
53             a.w = 0;
54             a.l = a.t = o;
55             a.t -= 1;
56             if(Roo.isIE){
57                 a.l -= this.offset + rad;
58                 a.t -= this.offset + rad;
59                 a.w -= rad;
60                 a.h -= rad;
61                 a.t += 1;
62             }
63         break;
64         case "sides":
65             a.w = (o*2);
66             a.l = -o;
67             a.t = o-1;
68             if(Roo.isIE){
69                 a.l -= (this.offset - rad);
70                 a.t -= this.offset + rad;
71                 a.l += 1;
72                 a.w -= (this.offset - rad)*2;
73                 a.w -= rad + 1;
74                 a.h -= 1;
75             }
76         break;
77         case "frame":
78             a.w = a.h = (o*2);
79             a.l = a.t = -o;
80             a.t += 1;
81             a.h -= 2;
82             if(Roo.isIE){
83                 a.l -= (this.offset - rad);
84                 a.t -= (this.offset - rad);
85                 a.l += 1;
86                 a.w -= (this.offset + rad + 1);
87                 a.h -= (this.offset + rad);
88                 a.h += 1;
89             }
90         break;
91     };
92
93     this.adjusts = a;
94 };
95
96 Roo.Shadow.prototype = {
97     /**
98      * @cfg {String} mode
99      * The shadow display mode.  Supports the following options:<br />
100      * sides: Shadow displays on both sides and bottom only<br />
101      * frame: Shadow displays equally on all four sides<br />
102      * drop: Traditional bottom-right drop shadow (default)
103      */
104     mode: false,
105     /**
106      * @cfg {String} offset
107      * The number of pixels to offset the shadow from the element (defaults to 4)
108      */
109     offset: 4,
110
111     // private
112     defaultMode: "drop",
113
114     /**
115      * Displays the shadow under the target element
116      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
117      */
118     show : function(target){
119         target = Roo.get(target);
120         if(!this.el){
121             this.el = Roo.Shadow.Pool.pull();
122             if(this.el.dom.nextSibling != target.dom){
123                 this.el.insertBefore(target);
124             }
125         }
126         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
127         if(Roo.isIE){
128             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
129         }
130         this.realign(
131             target.getLeft(true),
132             target.getTop(true),
133             target.getWidth(),
134             target.getHeight()
135         );
136         this.el.dom.style.display = "block";
137     },
138
139     /**
140      * Returns true if the shadow is visible, else false
141      */
142     isVisible : function(){
143         return this.el ? true : false;  
144     },
145
146     /**
147      * Direct alignment when values are already available. Show must be called at least once before
148      * calling this method to ensure it is initialized.
149      * @param {Number} left The target element left position
150      * @param {Number} top The target element top position
151      * @param {Number} width The target element width
152      * @param {Number} height The target element height
153      */
154     realign : function(l, t, w, h){
155         if(!this.el){
156             return;
157         }
158         var a = this.adjusts, d = this.el.dom, s = d.style;
159         var iea = 0;
160         s.left = (l+a.l)+"px";
161         s.top = (t+a.t)+"px";
162         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
163  
164         if(s.width != sws || s.height != shs){
165             s.width = sws;
166             s.height = shs;
167             if(!Roo.isIE){
168                 var cn = d.childNodes;
169                 var sww = Math.max(0, (sw-12))+"px";
170                 cn[0].childNodes[1].style.width = sww;
171                 cn[1].childNodes[1].style.width = sww;
172                 cn[2].childNodes[1].style.width = sww;
173                 cn[1].style.height = Math.max(0, (sh-12))+"px";
174             }
175         }
176     },
177
178     /**
179      * Hides this shadow
180      */
181     hide : function(){
182         if(this.el){
183             this.el.dom.style.display = "none";
184             Roo.Shadow.Pool.push(this.el);
185             delete this.el;
186         }
187     },
188
189     /**
190      * Adjust the z-index of this shadow
191      * @param {Number} zindex The new z-index
192      */
193     setZIndex : function(z){
194         this.zIndex = z;
195         if(this.el){
196             this.el.setStyle("z-index", z);
197         }
198     }
199 };
200
201 // Private utility class that manages the internal Shadow cache
202 Roo.Shadow.Pool = function(){
203     var p = [];
204     var markup = Roo.isIE ?
205                  '<div class="x-ie-shadow"></div>' :
206                  '<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>';
207     return {
208         pull : function(){
209             var sh = p.shift();
210             if(!sh){
211                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
212                 sh.autoBoxAdjust = false;
213             }
214             return sh;
215         },
216
217         push : function(sh){
218             p.push(sh);
219         }
220     };
221 }();/*
222  * - LGPL
223  *
224  * base class for bootstrap elements.
225  * 
226  */
227
228 Roo.bootstrap = Roo.bootstrap || {};
229 /**
230  * @class Roo.bootstrap.Component
231  * @extends Roo.Component
232  * @abstract
233  * @children Roo.bootstrap.Component
234  * Bootstrap Component base class
235  * @cfg {String} cls css class
236  * @cfg {String} style any extra css
237  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
238  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
239  * @cfg {string} dataId cutomer id
240  * @cfg {string} name Specifies name attribute
241  * @cfg {string} tooltip  Text for the tooltip
242  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
243  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
244  
245  * @constructor
246  * Do not use directly - it does not do anything..
247  * @param {Object} config The config object
248  */
249
250
251
252 Roo.bootstrap.Component = function(config){
253     Roo.bootstrap.Component.superclass.constructor.call(this, config);
254        
255     this.addEvents({
256         /**
257          * @event childrenrendered
258          * Fires when the children have been rendered..
259          * @param {Roo.bootstrap.Component} this
260          */
261         "childrenrendered" : true
262         
263         
264         
265     });
266     
267     
268 };
269
270 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
271     
272     
273     allowDomMove : false, // to stop relocations in parent onRender...
274     
275     cls : false,
276     
277     style : false,
278     
279     autoCreate : false,
280     
281     tooltip : null,
282     /**
283      * Initialize Events for the element
284      */
285     initEvents : function() { },
286     
287     xattr : false,
288     
289     parentId : false,
290     
291     can_build_overlaid : true,
292     
293     container_method : false,
294     
295     dataId : false,
296     
297     name : false,
298     
299     parent: function() {
300         // returns the parent component..
301         return Roo.ComponentMgr.get(this.parentId)
302         
303         
304     },
305     
306     // private
307     onRender : function(ct, position)
308     {
309        // Roo.log("Call onRender: " + this.xtype);
310         
311         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
312         
313         if(this.el){
314             if (this.el.attr('xtype')) {
315                 this.el.attr('xtypex', this.el.attr('xtype'));
316                 this.el.dom.removeAttribute('xtype');
317                 
318                 this.initEvents();
319             }
320             
321             return;
322         }
323         
324          
325         
326         var cfg = Roo.apply({},  this.getAutoCreate());
327         
328         cfg.id = this.id || Roo.id();
329         
330         // fill in the extra attributes 
331         if (this.xattr && typeof(this.xattr) =='object') {
332             for (var i in this.xattr) {
333                 cfg[i] = this.xattr[i];
334             }
335         }
336         
337         if(this.dataId){
338             cfg.dataId = this.dataId;
339         }
340         
341         if (this.cls) {
342             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
343         }
344         
345         if (this.style) { // fixme needs to support more complex style data.
346             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
347         }
348         
349         if(this.name){
350             cfg.name = this.name;
351         }
352         
353         this.el = ct.createChild(cfg, position);
354         
355         if (this.tooltip) {
356             this.tooltipEl().attr('tooltip', this.tooltip);
357         }
358         
359         if(this.tabIndex !== undefined){
360             this.el.dom.setAttribute('tabIndex', this.tabIndex);
361         }
362         
363         this.initEvents();
364         
365     },
366     /**
367      * Fetch the element to add children to
368      * @return {Roo.Element} defaults to this.el
369      */
370     getChildContainer : function()
371     {
372         return this.el;
373     },
374     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
375     {
376         return Roo.get(document.body);
377     },
378     
379     /**
380      * Fetch the element to display the tooltip on.
381      * @return {Roo.Element} defaults to this.el
382      */
383     tooltipEl : function()
384     {
385         return this.el;
386     },
387         
388     addxtype  : function(tree,cntr)
389     {
390         var cn = this;
391         
392         cn = Roo.factory(tree);
393         //Roo.log(['addxtype', cn]);
394            
395         cn.parentType = this.xtype; //??
396         cn.parentId = this.id;
397         
398         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
399         if (typeof(cn.container_method) == 'string') {
400             cntr = cn.container_method;
401         }
402         
403         
404         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
405         
406         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
407         
408         var build_from_html =  Roo.XComponent.build_from_html;
409           
410         var is_body  = (tree.xtype == 'Body') ;
411           
412         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
413           
414         var self_cntr_el = Roo.get(this[cntr](false));
415         
416         // do not try and build conditional elements 
417         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
418             return false;
419         }
420         
421         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
422             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
423                 return this.addxtypeChild(tree,cntr, is_body);
424             }
425             
426             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
427                 
428             if(echild){
429                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
430             }
431             
432             Roo.log('skipping render');
433             return cn;
434             
435         }
436         
437         var ret = false;
438         if (!build_from_html) {
439             return false;
440         }
441         
442         // this i think handles overlaying multiple children of the same type
443         // with the sam eelement.. - which might be buggy..
444         while (true) {
445             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
446             
447             if (!echild) {
448                 break;
449             }
450             
451             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
452                 break;
453             }
454             
455             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
456         }
457        
458         return ret;
459     },
460     
461     
462     addxtypeChild : function (tree, cntr, is_body)
463     {
464         Roo.debug && Roo.log('addxtypeChild:' + cntr);
465         var cn = this;
466         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
467         
468         
469         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
470                     (typeof(tree['flexy:foreach']) != 'undefined');
471           
472     
473         
474         skip_children = false;
475         // render the element if it's not BODY.
476         if (!is_body) {
477             
478             // if parent was disabled, then do not try and create the children..
479             if(!this[cntr](true)){
480                 tree.items = [];
481                 return tree;
482             }
483            
484             cn = Roo.factory(tree);
485            
486             cn.parentType = this.xtype; //??
487             cn.parentId = this.id;
488             
489             var build_from_html =  Roo.XComponent.build_from_html;
490             
491             
492             // does the container contain child eleemnts with 'xtype' attributes.
493             // that match this xtype..
494             // note - when we render we create these as well..
495             // so we should check to see if body has xtype set.
496             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
497                
498                 var self_cntr_el = Roo.get(this[cntr](false));
499                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
500                 if (echild) { 
501                     //Roo.log(Roo.XComponent.build_from_html);
502                     //Roo.log("got echild:");
503                     //Roo.log(echild);
504                 }
505                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
506                 // and are not displayed -this causes this to use up the wrong element when matching.
507                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
508                 
509                 
510                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
511                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
512                   
513                   
514                   
515                     cn.el = echild;
516                   //  Roo.log("GOT");
517                     //echild.dom.removeAttribute('xtype');
518                 } else {
519                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
520                     Roo.debug && Roo.log(self_cntr_el);
521                     Roo.debug && Roo.log(echild);
522                     Roo.debug && Roo.log(cn);
523                 }
524             }
525            
526             
527            
528             // if object has flexy:if - then it may or may not be rendered.
529             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
530                 // skip a flexy if element.
531                 Roo.debug && Roo.log('skipping render');
532                 Roo.debug && Roo.log(tree);
533                 if (!cn.el) {
534                     Roo.debug && Roo.log('skipping all children');
535                     skip_children = true;
536                 }
537                 
538              } else {
539                  
540                 // actually if flexy:foreach is found, we really want to create 
541                 // multiple copies here...
542                 //Roo.log('render');
543                 //Roo.log(this[cntr]());
544                 // some elements do not have render methods.. like the layouts...
545                 /*
546                 if(this[cntr](true) === false){
547                     cn.items = [];
548                     return cn;
549                 }
550                 */
551                 cn.render && cn.render(this[cntr](true));
552                 
553              }
554             // then add the element..
555         }
556          
557         // handle the kids..
558         
559         var nitems = [];
560         /*
561         if (typeof (tree.menu) != 'undefined') {
562             tree.menu.parentType = cn.xtype;
563             tree.menu.triggerEl = cn.el;
564             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
565             
566         }
567         */
568         if (!tree.items || !tree.items.length) {
569             cn.items = nitems;
570             //Roo.log(["no children", this]);
571             
572             return cn;
573         }
574          
575         var items = tree.items;
576         delete tree.items;
577         
578         //Roo.log(items.length);
579             // add the items..
580         if (!skip_children) {    
581             for(var i =0;i < items.length;i++) {
582               //  Roo.log(['add child', items[i]]);
583                 nitems.push(cn.addxtype(items[i].xns == false ? items[i] : Roo.apply({}, items[i])));
584             }
585         }
586         
587         cn.items = nitems;
588         
589         //Roo.log("fire childrenrendered");
590         
591         cn.fireEvent('childrenrendered', this);
592         
593         return cn;
594     },
595     
596     /**
597      * Set the element that will be used to show or hide
598      */
599     setVisibilityEl : function(el)
600     {
601         this.visibilityEl = el;
602     },
603     
604      /**
605      * Get the element that will be used to show or hide
606      */
607     getVisibilityEl : function()
608     {
609         if (typeof(this.visibilityEl) == 'object') {
610             return this.visibilityEl;
611         }
612         
613         if (typeof(this.visibilityEl) == 'string') {
614             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
615         }
616         
617         return this.getEl();
618     },
619     
620     /**
621      * Show a component - removes 'hidden' class
622      */
623     show : function()
624     {
625         if(!this.getVisibilityEl()){
626             return;
627         }
628          
629         this.getVisibilityEl().removeClass(['hidden','d-none']);
630         
631         this.fireEvent('show', this);
632         
633         
634     },
635     /**
636      * Hide a component - adds 'hidden' class
637      */
638     hide: function()
639     {
640         if(!this.getVisibilityEl()){
641             return;
642         }
643         
644         this.getVisibilityEl().addClass(['hidden','d-none']);
645         
646         this.fireEvent('hide', this);
647         
648     }
649 });
650
651  /*
652  * - LGPL
653  *
654  * element
655  * 
656  */
657
658 /**
659  * @class Roo.bootstrap.Element
660  * @extends Roo.bootstrap.Component
661  * @children Roo.bootstrap.Component
662  * Bootstrap Element class (basically a DIV used to make random stuff )
663  * 
664  * @cfg {String} html contents of the element
665  * @cfg {String} tag tag of the element
666  * @cfg {String} cls class of the element
667  * @cfg {Boolean} preventDefault (true|false) default false
668  * @cfg {Boolean} clickable (true|false) default false
669  * @cfg {String} role default blank - set to button to force cursor pointer
670  
671  * 
672  * @constructor
673  * Create a new Element
674  * @param {Object} config The config object
675  */
676
677 Roo.bootstrap.Element = function(config){
678     Roo.bootstrap.Element.superclass.constructor.call(this, config);
679     
680     this.addEvents({
681         // raw events
682         /**
683          * @event click
684          * When a element is chick
685          * @param {Roo.bootstrap.Element} this
686          * @param {Roo.EventObject} e
687          */
688         "click" : true 
689         
690       
691     });
692 };
693
694 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
695     
696     tag: 'div',
697     cls: '',
698     html: '',
699     preventDefault: false, 
700     clickable: false,
701     tapedTwice : false,
702     role : false,
703     
704     getAutoCreate : function(){
705         
706         var cfg = {
707             tag: this.tag,
708             // cls: this.cls, double assign in parent class Component.js :: onRender
709             html: this.html
710         };
711         if (this.role !== false) {
712             cfg.role = this.role;
713         }
714         
715         return cfg;
716     },
717     
718     initEvents: function() 
719     {
720         Roo.bootstrap.Element.superclass.initEvents.call(this);
721         
722         if(this.clickable){
723             this.el.on('click', this.onClick, this);
724         }
725         
726         
727     },
728     
729     onClick : function(e)
730     {
731         if(this.preventDefault){
732             e.preventDefault();
733         }
734         
735         this.fireEvent('click', this, e); // why was this double click before?
736     },
737     
738     
739     
740
741     
742     
743     getValue : function()
744     {
745         return this.el.dom.innerHTML;
746     },
747     
748     setValue : function(value)
749     {
750         this.el.dom.innerHTML = value;
751     }
752    
753 });
754
755  
756
757  /*
758  * - LGPL
759  *
760  * dropable area
761  * 
762  */
763
764 /**
765  * @class Roo.bootstrap.DropTarget
766  * @extends Roo.bootstrap.Element
767  * Bootstrap DropTarget class
768  
769  * @cfg {string} name dropable name
770  * 
771  * @constructor
772  * Create a new Dropable Area
773  * @param {Object} config The config object
774  */
775
776 Roo.bootstrap.DropTarget = function(config){
777     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
778     
779     this.addEvents({
780         // raw events
781         /**
782          * @event click
783          * When a element is chick
784          * @param {Roo.bootstrap.Element} this
785          * @param {Roo.EventObject} e
786          */
787         "drop" : true
788     });
789 };
790
791 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
792     
793     
794     getAutoCreate : function(){
795         
796          
797     },
798     
799     initEvents: function() 
800     {
801         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
802         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
803             ddGroup: this.name,
804             listeners : {
805                 drop : this.dragDrop.createDelegate(this),
806                 enter : this.dragEnter.createDelegate(this),
807                 out : this.dragOut.createDelegate(this),
808                 over : this.dragOver.createDelegate(this)
809             }
810             
811         });
812         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
813     },
814     
815     dragDrop : function(source,e,data)
816     {
817         // user has to decide how to impliment this.
818         Roo.log('drop');
819         Roo.log(this);
820         //this.fireEvent('drop', this, source, e ,data);
821         return false;
822     },
823     
824     dragEnter : function(n, dd, e, data)
825     {
826         // probably want to resize the element to match the dropped element..
827         Roo.log("enter");
828         this.originalSize = this.el.getSize();
829         this.el.setSize( n.el.getSize());
830         this.dropZone.DDM.refreshCache(this.name);
831         Roo.log([n, dd, e, data]);
832     },
833     
834     dragOut : function(value)
835     {
836         // resize back to normal
837         Roo.log("out");
838         this.el.setSize(this.originalSize);
839         this.dropZone.resetConstraints();
840     },
841     
842     dragOver : function()
843     {
844         // ??? do nothing?
845     }
846    
847 });
848
849  
850
851  /*
852  * - LGPL
853  *
854  * Body
855  *
856  */
857
858 /**
859  * @class Roo.bootstrap.Body
860  * @extends Roo.bootstrap.Component
861  * @children Roo.bootstrap.Component 
862  * @parent none builder
863  * Bootstrap Body class
864  *
865  * @constructor
866  * Create a new body
867  * @param {Object} config The config object
868  */
869
870 Roo.bootstrap.Body = function(config){
871
872     config = config || {};
873
874     Roo.bootstrap.Body.superclass.constructor.call(this, config);
875     this.el = Roo.get(config.el ? config.el : document.body );
876     if (this.cls && this.cls.length) {
877         Roo.get(document.body).addClass(this.cls);
878     }
879 };
880
881 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
882
883     is_body : true,// just to make sure it's constructed?
884
885         autoCreate : {
886         cls: 'container'
887     },
888     onRender : function(ct, position)
889     {
890        /* Roo.log("Roo.bootstrap.Body - onRender");
891         if (this.cls && this.cls.length) {
892             Roo.get(document.body).addClass(this.cls);
893         }
894         // style??? xttr???
895         */
896     }
897
898
899
900
901 });
902 /*
903  * - LGPL
904  *
905  * button group
906  * 
907  */
908
909
910 /**
911  * @class Roo.bootstrap.ButtonGroup
912  * @extends Roo.bootstrap.Component
913  * Bootstrap ButtonGroup class
914  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
915  * 
916  * @cfg {String} size lg | sm | xs (default empty normal)
917  * @cfg {String} align vertical | justified  (default none)
918  * @cfg {String} direction up | down (default down)
919  * @cfg {Boolean} toolbar false | true
920  * @cfg {Boolean} btn true | false
921  * 
922  * 
923  * @constructor
924  * Create a new Input
925  * @param {Object} config The config object
926  */
927
928 Roo.bootstrap.ButtonGroup = function(config){
929     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
930 };
931
932 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
933     
934     size: '',
935     align: '',
936     direction: '',
937     toolbar: false,
938     btn: true,
939
940     getAutoCreate : function(){
941         var cfg = {
942             cls: 'btn-group',
943             html : null
944         };
945         
946         cfg.html = this.html || cfg.html;
947         
948         if (this.toolbar) {
949             cfg = {
950                 cls: 'btn-toolbar',
951                 html: null
952             };
953             
954             return cfg;
955         }
956         
957         if (['vertical','justified'].indexOf(this.align)!==-1) {
958             cfg.cls = 'btn-group-' + this.align;
959             
960             if (this.align == 'justified') {
961                 console.log(this.items);
962             }
963         }
964         
965         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
966             cfg.cls += ' btn-group-' + this.size;
967         }
968         
969         if (this.direction == 'up') {
970             cfg.cls += ' dropup' ;
971         }
972         
973         return cfg;
974     },
975     /**
976      * Add a button to the group (similar to NavItem API.)
977      */
978     addItem : function(cfg)
979     {
980         var cn = new Roo.bootstrap.Button(cfg);
981         //this.register(cn);
982         cn.parentId = this.id;
983         cn.onRender(this.el, null);
984         return cn;
985     }
986    
987 });
988
989  /*
990  * - LGPL
991  *
992  * button
993  * 
994  */
995
996 /**
997  * @class Roo.bootstrap.Button
998  * @extends Roo.bootstrap.Component
999  * Bootstrap Button class
1000  * @cfg {String} html The button content
1001  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1002  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1003  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1004  * @cfg {String} size (lg|sm|xs)
1005  * @cfg {String} tag (a|input|submit)
1006  * @cfg {String} href empty or href
1007  * @cfg {Boolean} disabled default false;
1008  * @cfg {Boolean} isClose default false;
1009  * @cfg {String} glyphicon depricated - use fa
1010  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1011  * @cfg {String} badge text for badge
1012  * @cfg {String} theme (default|glow)  
1013  * @cfg {Boolean} inverse dark themed version
1014  * @cfg {Boolean} toggle is it a slidy toggle button
1015  * @cfg {Boolean} pressed   default null - if the button ahs active state
1016  * @cfg {String} ontext text for on slidy toggle state
1017  * @cfg {String} offtext text for off slidy toggle state
1018  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1019  * @cfg {Boolean} removeClass remove the standard class..
1020  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1021  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1022  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1023
1024  * @constructor
1025  * Create a new button
1026  * @param {Object} config The config object
1027  */
1028
1029
1030 Roo.bootstrap.Button = function(config){
1031     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1032     
1033     this.addEvents({
1034         // raw events
1035         /**
1036          * @event click
1037          * When a button is pressed
1038          * @param {Roo.bootstrap.Button} btn
1039          * @param {Roo.EventObject} e
1040          */
1041         "click" : true,
1042         /**
1043          * @event dblclick
1044          * When a button is double clicked
1045          * @param {Roo.bootstrap.Button} btn
1046          * @param {Roo.EventObject} e
1047          */
1048         "dblclick" : true,
1049          /**
1050          * @event toggle
1051          * After the button has been toggles
1052          * @param {Roo.bootstrap.Button} btn
1053          * @param {Roo.EventObject} e
1054          * @param {boolean} pressed (also available as button.pressed)
1055          */
1056         "toggle" : true
1057     });
1058 };
1059
1060 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1061     html: false,
1062     active: false,
1063     weight: '',
1064     badge_weight: '',
1065     outline : false,
1066     size: '',
1067     tag: 'button',
1068     href: '',
1069     disabled: false,
1070     isClose: false,
1071     glyphicon: '',
1072     fa: '',
1073     badge: '',
1074     theme: 'default',
1075     inverse: false,
1076     
1077     toggle: false,
1078     ontext: 'ON',
1079     offtext: 'OFF',
1080     defaulton: true,
1081     preventDefault: true,
1082     removeClass: false,
1083     name: false,
1084     target: false,
1085     group : false,
1086      
1087     pressed : null,
1088      
1089     
1090     getAutoCreate : function(){
1091         
1092         var cfg = {
1093             tag : 'button',
1094             cls : 'roo-button',
1095             html: ''
1096         };
1097         
1098         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1099             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1100             this.tag = 'button';
1101         } else {
1102             cfg.tag = this.tag;
1103         }
1104         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1105         
1106         if (this.toggle == true) {
1107             cfg={
1108                 tag: 'div',
1109                 cls: 'slider-frame roo-button',
1110                 cn: [
1111                     {
1112                         tag: 'span',
1113                         'data-on-text':'ON',
1114                         'data-off-text':'OFF',
1115                         cls: 'slider-button',
1116                         html: this.offtext
1117                     }
1118                 ]
1119             };
1120             // why are we validating the weights?
1121             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1122                 cfg.cls +=  ' ' + this.weight;
1123             }
1124             
1125             return cfg;
1126         }
1127         
1128         if (this.isClose) {
1129             cfg.cls += ' close';
1130             
1131             cfg["aria-hidden"] = true;
1132             
1133             cfg.html = "&times;";
1134             
1135             return cfg;
1136         }
1137              
1138         
1139         if (this.theme==='default') {
1140             cfg.cls = 'btn roo-button';
1141             
1142             //if (this.parentType != 'Navbar') {
1143             this.weight = this.weight.length ?  this.weight : 'default';
1144             //}
1145             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146                 
1147                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1148                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1149                 cfg.cls += ' btn-' + outline + weight;
1150                 if (this.weight == 'default') {
1151                     // BC
1152                     cfg.cls += ' btn-' + this.weight;
1153                 }
1154             }
1155         } else if (this.theme==='glow') {
1156             
1157             cfg.tag = 'a';
1158             cfg.cls = 'btn-glow roo-button';
1159             
1160             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1161                 
1162                 cfg.cls += ' ' + this.weight;
1163             }
1164         }
1165    
1166         
1167         if (this.inverse) {
1168             this.cls += ' inverse';
1169         }
1170         
1171         
1172         if (this.active || this.pressed === true) {
1173             cfg.cls += ' active';
1174         }
1175         
1176         if (this.disabled) {
1177             cfg.disabled = 'disabled';
1178         }
1179         
1180         if (this.items) {
1181             Roo.log('changing to ul' );
1182             cfg.tag = 'ul';
1183             this.glyphicon = 'caret';
1184             if (Roo.bootstrap.version == 4) {
1185                 this.fa = 'caret-down';
1186             }
1187             
1188         }
1189         
1190         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1191          
1192         //gsRoo.log(this.parentType);
1193         if (this.parentType === 'Navbar' && !this.parent().bar) {
1194             Roo.log('changing to li?');
1195             
1196             cfg.tag = 'li';
1197             
1198             cfg.cls = '';
1199             cfg.cn =  [{
1200                 tag : 'a',
1201                 cls : 'roo-button',
1202                 html : this.html,
1203                 href : this.href || '#'
1204             }];
1205             if (this.menu) {
1206                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1207                 cfg.cls += ' dropdown';
1208             }   
1209             
1210             delete cfg.html;
1211             
1212         }
1213         
1214        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1215         
1216         if (this.glyphicon) {
1217             cfg.html = ' ' + cfg.html;
1218             
1219             cfg.cn = [
1220                 {
1221                     tag: 'span',
1222                     cls: 'glyphicon glyphicon-' + this.glyphicon
1223                 }
1224             ];
1225         }
1226         if (this.fa) {
1227             cfg.html = ' ' + cfg.html;
1228             
1229             cfg.cn = [
1230                 {
1231                     tag: 'i',
1232                     cls: 'fa fas fa-' + this.fa
1233                 }
1234             ];
1235         }
1236         
1237         if (this.badge) {
1238             cfg.html += ' ';
1239             
1240             cfg.tag = 'a';
1241             
1242 //            cfg.cls='btn roo-button';
1243             
1244             cfg.href=this.href;
1245             
1246             var value = cfg.html;
1247             
1248             if(this.glyphicon){
1249                 value = {
1250                     tag: 'span',
1251                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1252                     html: this.html
1253                 };
1254             }
1255             if(this.fa){
1256                 value = {
1257                     tag: 'i',
1258                     cls: 'fa fas fa-' + this.fa,
1259                     html: this.html
1260                 };
1261             }
1262             
1263             var bw = this.badge_weight.length ? this.badge_weight :
1264                 (this.weight.length ? this.weight : 'secondary');
1265             bw = bw == 'default' ? 'secondary' : bw;
1266             
1267             cfg.cn = [
1268                 value,
1269                 {
1270                     tag: 'span',
1271                     cls: 'badge badge-' + bw,
1272                     html: this.badge
1273                 }
1274             ];
1275             
1276             cfg.html='';
1277         }
1278         
1279         if (this.menu) {
1280             cfg.cls += ' dropdown';
1281             cfg.html = typeof(cfg.html) != 'undefined' ?
1282                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1283         }
1284         
1285         if (cfg.tag !== 'a' && this.href !== '') {
1286             throw "Tag must be a to set href.";
1287         } else if (this.href.length > 0) {
1288             cfg.href = this.href;
1289         }
1290         
1291         if(this.removeClass){
1292             cfg.cls = '';
1293         }
1294         
1295         if(this.target){
1296             cfg.target = this.target;
1297         }
1298         
1299         return cfg;
1300     },
1301     initEvents: function() {
1302        // Roo.log('init events?');
1303 //        Roo.log(this.el.dom);
1304         // add the menu...
1305         
1306         if (typeof (this.menu) != 'undefined') {
1307             this.menu.parentType = this.xtype;
1308             this.menu.triggerEl = this.el;
1309             this.addxtype(Roo.apply({}, this.menu));
1310         }
1311
1312
1313         if (this.el.hasClass('roo-button')) {
1314              this.el.on('click', this.onClick, this);
1315              this.el.on('dblclick', this.onDblClick, this);
1316         } else {
1317              this.el.select('.roo-button').on('click', this.onClick, this);
1318              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1319              
1320         }
1321         // why?
1322         if(this.removeClass){
1323             this.el.on('click', this.onClick, this);
1324         }
1325         
1326         if (this.group === true) {
1327              if (this.pressed === false || this.pressed === true) {
1328                 // nothing
1329             } else {
1330                 this.pressed = false;
1331                 this.setActive(this.pressed);
1332             }
1333             
1334         }
1335         
1336         this.el.enableDisplayMode();
1337         
1338     },
1339     onClick : function(e)
1340     {
1341         if (this.disabled) {
1342             return;
1343         }
1344         
1345         Roo.log('button on click ');
1346         if(this.href === '' || this.preventDefault){
1347             e.preventDefault();
1348         }
1349         
1350         if (this.group) {
1351             if (this.pressed) {
1352                 // do nothing -
1353                 return;
1354             }
1355             this.setActive(true);
1356             var pi = this.parent().items;
1357             for (var i = 0;i < pi.length;i++) {
1358                 if (this == pi[i]) {
1359                     continue;
1360                 }
1361                 if (pi[i].el.hasClass('roo-button')) {
1362                     pi[i].setActive(false);
1363                 }
1364             }
1365             this.fireEvent('click', this, e);            
1366             return;
1367         }
1368         
1369         if (this.pressed === true || this.pressed === false) {
1370             this.toggleActive(e);
1371         }
1372         
1373         
1374         this.fireEvent('click', this, e);
1375     },
1376     onDblClick: function(e)
1377     {
1378         if (this.disabled) {
1379             return;
1380         }
1381         if(this.preventDefault){
1382             e.preventDefault();
1383         }
1384         this.fireEvent('dblclick', this, e);
1385     },
1386     /**
1387      * Enables this button
1388      */
1389     enable : function()
1390     {
1391         this.disabled = false;
1392         this.el.removeClass('disabled');
1393         this.el.dom.removeAttribute("disabled");
1394     },
1395     
1396     /**
1397      * Disable this button
1398      */
1399     disable : function()
1400     {
1401         this.disabled = true;
1402         this.el.addClass('disabled');
1403         this.el.attr("disabled", "disabled")
1404     },
1405      /**
1406      * sets the active state on/off, 
1407      * @param {Boolean} state (optional) Force a particular state
1408      */
1409     setActive : function(v) {
1410         
1411         this.el[v ? 'addClass' : 'removeClass']('active');
1412         this.pressed = v;
1413     },
1414      /**
1415      * toggles the current active state 
1416      */
1417     toggleActive : function(e)
1418     {
1419         this.setActive(!this.pressed); // this modifies pressed...
1420         this.fireEvent('toggle', this, e, this.pressed);
1421     },
1422      /**
1423      * get the current active state
1424      * @return {boolean} true if it's active
1425      */
1426     isActive : function()
1427     {
1428         return this.el.hasClass('active');
1429     },
1430     /**
1431      * set the text of the first selected button
1432      */
1433     setText : function(str)
1434     {
1435         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1436     },
1437     /**
1438      * get the text of the first selected button
1439      */
1440     getText : function()
1441     {
1442         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1443     },
1444     
1445     setWeight : function(str)
1446     {
1447         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1448         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1449         this.weight = str;
1450         var outline = this.outline ? 'outline-' : '';
1451         if (str == 'default') {
1452             this.el.addClass('btn-default btn-outline-secondary');        
1453             return;
1454         }
1455         this.el.addClass('btn-' + outline + str);        
1456     }
1457     
1458     
1459 });
1460 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1461
1462 Roo.bootstrap.Button.weights = [
1463     'default',
1464     'secondary' ,
1465     'primary',
1466     'success',
1467     'info',
1468     'warning',
1469     'danger',
1470     'link',
1471     'light',
1472     'dark'              
1473    
1474 ];/*
1475  * - LGPL
1476  *
1477  * column
1478  * 
1479  */
1480
1481 /**
1482  * @class Roo.bootstrap.Column
1483  * @extends Roo.bootstrap.Component
1484  * @children Roo.bootstrap.Component
1485  * Bootstrap Column class
1486  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1487  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1488  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1489  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1490  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1491  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1492  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1493  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1494  *
1495  * 
1496  * @cfg {Boolean} hidden (true|false) hide the element
1497  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1498  * @cfg {String} fa (ban|check|...) font awesome icon
1499  * @cfg {Number} fasize (1|2|....) font awsome size
1500
1501  * @cfg {String} icon (info-sign|check|...) glyphicon name
1502
1503  * @cfg {String} html content of column.
1504  * 
1505  * @constructor
1506  * Create a new Column
1507  * @param {Object} config The config object
1508  */
1509
1510 Roo.bootstrap.Column = function(config){
1511     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1512 };
1513
1514 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1515     
1516     xs: false,
1517     sm: false,
1518     md: false,
1519     lg: false,
1520     xsoff: false,
1521     smoff: false,
1522     mdoff: false,
1523     lgoff: false,
1524     html: '',
1525     offset: 0,
1526     alert: false,
1527     fa: false,
1528     icon : false,
1529     hidden : false,
1530     fasize : 1,
1531     
1532     getAutoCreate : function(){
1533         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1534         
1535         cfg = {
1536             tag: 'div',
1537             cls: 'column'
1538         };
1539         
1540         var settings=this;
1541         var sizes =   ['xs','sm','md','lg'];
1542         sizes.map(function(size ,ix){
1543             //Roo.log( size + ':' + settings[size]);
1544             
1545             if (settings[size+'off'] !== false) {
1546                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1547             }
1548             
1549             if (settings[size] === false) {
1550                 return;
1551             }
1552             
1553             if (!settings[size]) { // 0 = hidden
1554                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1555                 // bootsrap4
1556                 for (var i = ix; i > -1; i--) {
1557                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1558                 }
1559                 
1560                 
1561                 return;
1562             }
1563             cfg.cls += ' col-' + size + '-' + settings[size] + (
1564                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1565             );
1566             
1567         });
1568         
1569         if (this.hidden) {
1570             cfg.cls += ' hidden';
1571         }
1572         
1573         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1574             cfg.cls +=' alert alert-' + this.alert;
1575         }
1576         
1577         
1578         if (this.html.length) {
1579             cfg.html = this.html;
1580         }
1581         if (this.fa) {
1582             var fasize = '';
1583             if (this.fasize > 1) {
1584                 fasize = ' fa-' + this.fasize + 'x';
1585             }
1586             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1587             
1588             
1589         }
1590         if (this.icon) {
1591             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1592         }
1593         
1594         return cfg;
1595     }
1596    
1597 });
1598
1599  
1600
1601  /*
1602  * - LGPL
1603  *
1604  * page container.
1605  * 
1606  */
1607
1608
1609 /**
1610  * @class Roo.bootstrap.Container
1611  * @extends Roo.bootstrap.Component
1612  * @children Roo.bootstrap.Component
1613  * @parent builder
1614  * Bootstrap Container class
1615  * @cfg {Boolean} jumbotron is it a jumbotron element
1616  * @cfg {String} html content of element
1617  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1618  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1619  * @cfg {String} header content of header (for panel)
1620  * @cfg {String} footer content of footer (for panel)
1621  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1622  * @cfg {String} tag (header|aside|section) type of HTML tag.
1623  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1624  * @cfg {String} fa font awesome icon
1625  * @cfg {String} icon (info-sign|check|...) glyphicon name
1626  * @cfg {Boolean} hidden (true|false) hide the element
1627  * @cfg {Boolean} expandable (true|false) default false
1628  * @cfg {Boolean} expanded (true|false) default true
1629  * @cfg {String} rheader contet on the right of header
1630  * @cfg {Boolean} clickable (true|false) default false
1631
1632  *     
1633  * @constructor
1634  * Create a new Container
1635  * @param {Object} config The config object
1636  */
1637
1638 Roo.bootstrap.Container = function(config){
1639     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1640     
1641     this.addEvents({
1642         // raw events
1643          /**
1644          * @event expand
1645          * After the panel has been expand
1646          * 
1647          * @param {Roo.bootstrap.Container} this
1648          */
1649         "expand" : true,
1650         /**
1651          * @event collapse
1652          * After the panel has been collapsed
1653          * 
1654          * @param {Roo.bootstrap.Container} this
1655          */
1656         "collapse" : true,
1657         /**
1658          * @event click
1659          * When a element is chick
1660          * @param {Roo.bootstrap.Container} this
1661          * @param {Roo.EventObject} e
1662          */
1663         "click" : true
1664     });
1665 };
1666
1667 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1668     
1669     jumbotron : false,
1670     well: '',
1671     panel : '',
1672     header: '',
1673     footer : '',
1674     sticky: '',
1675     tag : false,
1676     alert : false,
1677     fa: false,
1678     icon : false,
1679     expandable : false,
1680     rheader : '',
1681     expanded : true,
1682     clickable: false,
1683   
1684      
1685     getChildContainer : function() {
1686         
1687         if(!this.el){
1688             return false;
1689         }
1690         
1691         if (this.panel.length) {
1692             return this.el.select('.panel-body',true).first();
1693         }
1694         
1695         return this.el;
1696     },
1697     
1698     
1699     getAutoCreate : function(){
1700         
1701         var cfg = {
1702             tag : this.tag || 'div',
1703             html : '',
1704             cls : ''
1705         };
1706         if (this.jumbotron) {
1707             cfg.cls = 'jumbotron';
1708         }
1709         
1710         
1711         
1712         // - this is applied by the parent..
1713         //if (this.cls) {
1714         //    cfg.cls = this.cls + '';
1715         //}
1716         
1717         if (this.sticky.length) {
1718             
1719             var bd = Roo.get(document.body);
1720             if (!bd.hasClass('bootstrap-sticky')) {
1721                 bd.addClass('bootstrap-sticky');
1722                 Roo.select('html',true).setStyle('height', '100%');
1723             }
1724              
1725             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1726         }
1727         
1728         
1729         if (this.well.length) {
1730             switch (this.well) {
1731                 case 'lg':
1732                 case 'sm':
1733                     cfg.cls +=' well well-' +this.well;
1734                     break;
1735                 default:
1736                     cfg.cls +=' well';
1737                     break;
1738             }
1739         }
1740         
1741         if (this.hidden) {
1742             cfg.cls += ' hidden';
1743         }
1744         
1745         
1746         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1747             cfg.cls +=' alert alert-' + this.alert;
1748         }
1749         
1750         var body = cfg;
1751         
1752         if (this.panel.length) {
1753             cfg.cls += ' panel panel-' + this.panel;
1754             cfg.cn = [];
1755             if (this.header.length) {
1756                 
1757                 var h = [];
1758                 
1759                 if(this.expandable){
1760                     
1761                     cfg.cls = cfg.cls + ' expandable';
1762                     
1763                     h.push({
1764                         tag: 'i',
1765                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1766                     });
1767                     
1768                 }
1769                 
1770                 h.push(
1771                     {
1772                         tag: 'span',
1773                         cls : 'panel-title',
1774                         html : (this.expandable ? '&nbsp;' : '') + this.header
1775                     },
1776                     {
1777                         tag: 'span',
1778                         cls: 'panel-header-right',
1779                         html: this.rheader
1780                     }
1781                 );
1782                 
1783                 cfg.cn.push({
1784                     cls : 'panel-heading',
1785                     style : this.expandable ? 'cursor: pointer' : '',
1786                     cn : h
1787                 });
1788                 
1789             }
1790             
1791             body = false;
1792             cfg.cn.push({
1793                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1794                 html : this.html
1795             });
1796             
1797             
1798             if (this.footer.length) {
1799                 cfg.cn.push({
1800                     cls : 'panel-footer',
1801                     html : this.footer
1802                     
1803                 });
1804             }
1805             
1806         }
1807         
1808         if (body) {
1809             body.html = this.html || cfg.html;
1810             // prefix with the icons..
1811             if (this.fa) {
1812                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1813             }
1814             if (this.icon) {
1815                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1816             }
1817             
1818             
1819         }
1820         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1821             cfg.cls =  'container';
1822         }
1823         
1824         return cfg;
1825     },
1826     
1827     initEvents: function() 
1828     {
1829         if(this.expandable){
1830             var headerEl = this.headerEl();
1831         
1832             if(headerEl){
1833                 headerEl.on('click', this.onToggleClick, this);
1834             }
1835         }
1836         
1837         if(this.clickable){
1838             this.el.on('click', this.onClick, this);
1839         }
1840         
1841     },
1842     
1843     onToggleClick : function()
1844     {
1845         var headerEl = this.headerEl();
1846         
1847         if(!headerEl){
1848             return;
1849         }
1850         
1851         if(this.expanded){
1852             this.collapse();
1853             return;
1854         }
1855         
1856         this.expand();
1857     },
1858     
1859     expand : function()
1860     {
1861         if(this.fireEvent('expand', this)) {
1862             
1863             this.expanded = true;
1864             
1865             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1866             
1867             this.el.select('.panel-body',true).first().removeClass('hide');
1868             
1869             var toggleEl = this.toggleEl();
1870
1871             if(!toggleEl){
1872                 return;
1873             }
1874
1875             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1876         }
1877         
1878     },
1879     
1880     collapse : function()
1881     {
1882         if(this.fireEvent('collapse', this)) {
1883             
1884             this.expanded = false;
1885             
1886             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1887             this.el.select('.panel-body',true).first().addClass('hide');
1888         
1889             var toggleEl = this.toggleEl();
1890
1891             if(!toggleEl){
1892                 return;
1893             }
1894
1895             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1896         }
1897     },
1898     
1899     toggleEl : function()
1900     {
1901         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1902             return;
1903         }
1904         
1905         return this.el.select('.panel-heading .fa',true).first();
1906     },
1907     
1908     headerEl : function()
1909     {
1910         if(!this.el || !this.panel.length || !this.header.length){
1911             return;
1912         }
1913         
1914         return this.el.select('.panel-heading',true).first()
1915     },
1916     
1917     bodyEl : function()
1918     {
1919         if(!this.el || !this.panel.length){
1920             return;
1921         }
1922         
1923         return this.el.select('.panel-body',true).first()
1924     },
1925     
1926     titleEl : function()
1927     {
1928         if(!this.el || !this.panel.length || !this.header.length){
1929             return;
1930         }
1931         
1932         return this.el.select('.panel-title',true).first();
1933     },
1934     
1935     setTitle : function(v)
1936     {
1937         var titleEl = this.titleEl();
1938         
1939         if(!titleEl){
1940             return;
1941         }
1942         
1943         titleEl.dom.innerHTML = v;
1944     },
1945     
1946     getTitle : function()
1947     {
1948         
1949         var titleEl = this.titleEl();
1950         
1951         if(!titleEl){
1952             return '';
1953         }
1954         
1955         return titleEl.dom.innerHTML;
1956     },
1957     
1958     setRightTitle : function(v)
1959     {
1960         var t = this.el.select('.panel-header-right',true).first();
1961         
1962         if(!t){
1963             return;
1964         }
1965         
1966         t.dom.innerHTML = v;
1967     },
1968     
1969     onClick : function(e)
1970     {
1971         e.preventDefault();
1972         
1973         this.fireEvent('click', this, e);
1974     }
1975 });
1976
1977  /**
1978  * @class Roo.bootstrap.Card
1979  * @extends Roo.bootstrap.Component
1980  * @children Roo.bootstrap.Component
1981  * @licence LGPL
1982  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1983  *
1984  *
1985  * possible... may not be implemented..
1986  * @cfg {String} header_image  src url of image.
1987  * @cfg {String|Object} header
1988  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1989  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1990  * 
1991  * @cfg {String} title
1992  * @cfg {String} subtitle
1993  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1994  * @cfg {String} footer
1995  
1996  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1997  * 
1998  * @cfg {String} margin (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2002  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2003  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2004  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2005  *
2006  * @cfg {String} padding (0|1|2|3|4|5)
2007  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2008  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2009  * @cfg {String} padding_left (0|1|2|3|4|5)
2010  * @cfg {String} padding_right (0|1|2|3|4|5)
2011  * @cfg {String} padding_x (0|1|2|3|4|5)
2012  * @cfg {String} padding_y (0|1|2|3|4|5)
2013  *
2014  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2018  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2019  
2020  * @config {Boolean} dragable  if this card can be dragged.
2021  * @config {String} drag_group  group for drag
2022  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2023  * @config {String} drop_group  group for drag
2024  * 
2025  * @config {Boolean} collapsable can the body be collapsed.
2026  * @config {Boolean} collapsed is the body collapsed when rendered...
2027  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2028  * @config {Boolean} rotated is the body rotated when rendered...
2029  * 
2030  * @constructor
2031  * Create a new Container
2032  * @param {Object} config The config object
2033  */
2034
2035 Roo.bootstrap.Card = function(config){
2036     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2037     
2038     this.addEvents({
2039          // raw events
2040         /**
2041          * @event drop
2042          * When a element a card is dropped
2043          * @param {Roo.bootstrap.Card} this
2044          *
2045          * 
2046          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2047          * @param {String} position 'above' or 'below'
2048          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2049         
2050          */
2051         'drop' : true,
2052          /**
2053          * @event rotate
2054          * When a element a card is rotate
2055          * @param {Roo.bootstrap.Card} this
2056          * @param {Roo.Element} n the node being dropped?
2057          * @param {Boolean} rotate status
2058          */
2059         'rotate' : true,
2060         /**
2061          * @event cardover
2062          * When a card element is dragged over ready to drop (return false to block dropable)
2063          * @param {Roo.bootstrap.Card} this
2064          * @param {Object} data from dragdrop 
2065          */
2066          'cardover' : true
2067          
2068     });
2069 };
2070
2071
2072 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2073     
2074     
2075     weight : '',
2076     
2077     margin: '', /// may be better in component?
2078     margin_top: '', 
2079     margin_bottom: '', 
2080     margin_left: '',
2081     margin_right: '',
2082     margin_x: '',
2083     margin_y: '',
2084     
2085     padding : '',
2086     padding_top: '', 
2087     padding_bottom: '', 
2088     padding_left: '',
2089     padding_right: '',
2090     padding_x: '',
2091     padding_y: '',
2092     
2093     display: '', 
2094     display_xs: '', 
2095     display_sm: '', 
2096     display_lg: '',
2097     display_xl: '',
2098  
2099     header_image  : '',
2100     header : '',
2101     header_size : 0,
2102     title : '',
2103     subtitle : '',
2104     html : '',
2105     footer: '',
2106
2107     collapsable : false,
2108     collapsed : false,
2109     rotateable : false,
2110     rotated : false,
2111     
2112     dragable : false,
2113     drag_group : false,
2114     dropable : false,
2115     drop_group : false,
2116     childContainer : false,
2117     dropEl : false, /// the dom placeholde element that indicates drop location.
2118     containerEl: false, // body container
2119     bodyEl: false, // card-body
2120     headerContainerEl : false, //
2121     headerEl : false,
2122     header_imageEl : false,
2123     
2124     
2125     layoutCls : function()
2126     {
2127         var cls = '';
2128         var t = this;
2129         Roo.log(this.margin_bottom.length);
2130         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2131             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2132             
2133             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2135             }
2136             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2137                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2138             }
2139         });
2140         
2141         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2142             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2143                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2144             }
2145         });
2146         
2147         // more generic support?
2148         if (this.hidden) {
2149             cls += ' d-none';
2150         }
2151         
2152         return cls;
2153     },
2154  
2155        // Roo.log("Call onRender: " + this.xtype);
2156         /*  We are looking at something like this.
2157 <div class="card">
2158     <img src="..." class="card-img-top" alt="...">
2159     <div class="card-body">
2160         <h5 class="card-title">Card title</h5>
2161          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2162
2163         >> this bit is really the body...
2164         <div> << we will ad dthis in hopefully it will not break shit.
2165         
2166         ** card text does not actually have any styling...
2167         
2168             <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>
2169         
2170         </div> <<
2171           <a href="#" class="card-link">Card link</a>
2172           
2173     </div>
2174     <div class="card-footer">
2175         <small class="text-muted">Last updated 3 mins ago</small>
2176     </div>
2177 </div>
2178          */
2179     getAutoCreate : function(){
2180         
2181         var cfg = {
2182             tag : 'div',
2183             cls : 'card',
2184             cn : [ ]
2185         };
2186         
2187         if (this.weight.length && this.weight != 'light') {
2188             cfg.cls += ' text-white';
2189         } else {
2190             cfg.cls += ' text-dark'; // need as it's nested..
2191         }
2192         if (this.weight.length) {
2193             cfg.cls += ' bg-' + this.weight;
2194         }
2195         
2196         cfg.cls += ' ' + this.layoutCls(); 
2197         
2198         var hdr = false;
2199         var hdr_ctr = false;
2200         if (this.header.length) {
2201             hdr = {
2202                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2203                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2204                 cn : []
2205             };
2206             cfg.cn.push(hdr);
2207             hdr_ctr = hdr;
2208         } else {
2209             hdr = {
2210                 tag : 'div',
2211                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2212                 cn : []
2213             };
2214             cfg.cn.push(hdr);
2215             hdr_ctr = hdr;
2216         }
2217         if (this.collapsable) {
2218             hdr_ctr = {
2219             tag : 'a',
2220             cls : 'd-block user-select-none',
2221             cn: [
2222                     {
2223                         tag: 'i',
2224                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2225                     }
2226                    
2227                 ]
2228             };
2229             hdr.cn.push(hdr_ctr);
2230         }
2231         
2232         hdr_ctr.cn.push(        {
2233             tag: 'span',
2234             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2235             html : this.header
2236         });
2237         
2238         
2239         if (this.header_image.length) {
2240             cfg.cn.push({
2241                 tag : 'img',
2242                 cls : 'card-img-top',
2243                 src: this.header_image // escape?
2244             });
2245         } else {
2246             cfg.cn.push({
2247                     tag : 'div',
2248                     cls : 'card-img-top d-none' 
2249                 });
2250         }
2251             
2252         var body = {
2253             tag : 'div',
2254             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2255             cn : []
2256         };
2257         var obody = body;
2258         if (this.collapsable || this.rotateable) {
2259             obody = {
2260                 tag: 'div',
2261                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2262                 cn : [  body ]
2263             };
2264         }
2265         
2266         cfg.cn.push(obody);
2267         
2268         if (this.title.length) {
2269             body.cn.push({
2270                 tag : 'div',
2271                 cls : 'card-title',
2272                 src: this.title // escape?
2273             });
2274         }  
2275         
2276         if (this.subtitle.length) {
2277             body.cn.push({
2278                 tag : 'div',
2279                 cls : 'card-title',
2280                 src: this.subtitle // escape?
2281             });
2282         }
2283         
2284         body.cn.push({
2285             tag : 'div',
2286             cls : 'roo-card-body-ctr'
2287         });
2288         
2289         if (this.html.length) {
2290             body.cn.push({
2291                 tag: 'div',
2292                 html : this.html
2293             });
2294         }
2295         // fixme ? handle objects?
2296         
2297         if (this.footer.length) {
2298            
2299             cfg.cn.push({
2300                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2301                 html : this.footer
2302             });
2303             
2304         } else {
2305             cfg.cn.push({cls : 'card-footer d-none'});
2306         }
2307         
2308         // footer...
2309         
2310         return cfg;
2311     },
2312     
2313     
2314     getCardHeader : function()
2315     {
2316         var  ret = this.el.select('.card-header',true).first();
2317         if (ret.hasClass('d-none')) {
2318             ret.removeClass('d-none');
2319         }
2320         
2321         return ret;
2322     },
2323     getCardFooter : function()
2324     {
2325         var  ret = this.el.select('.card-footer',true).first();
2326         if (ret.hasClass('d-none')) {
2327             ret.removeClass('d-none');
2328         }
2329         
2330         return ret;
2331     },
2332     getCardImageTop : function()
2333     {
2334         var  ret = this.header_imageEl;
2335         if (ret.hasClass('d-none')) {
2336             ret.removeClass('d-none');
2337         }
2338             
2339         return ret;
2340     },
2341     
2342     getChildContainer : function()
2343     {
2344         
2345         if(!this.el){
2346             return false;
2347         }
2348         return this.el.select('.roo-card-body-ctr',true).first();    
2349     },
2350     
2351     initEvents: function() 
2352     {
2353         this.bodyEl = this.el.select('.card-body',true).first(); 
2354         this.containerEl = this.getChildContainer();
2355         if(this.dragable){
2356             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2357                     containerScroll: true,
2358                     ddGroup: this.drag_group || 'default_card_drag_group'
2359             });
2360             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2361         }
2362         if (this.dropable) {
2363             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2364                 containerScroll: true,
2365                 ddGroup: this.drop_group || 'default_card_drag_group'
2366             });
2367             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2368             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2369             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2370             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2371             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2372         }
2373         
2374         if (this.collapsable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2376         }
2377         if (this.rotateable) {
2378             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2379         }
2380         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2381          
2382         this.footerEl = this.el.select('.card-footer',true).first();
2383         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2384         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2385         this.headerEl = this.el.select('.card-header',true).first();
2386         
2387         if (this.rotated) {
2388             this.el.addClass('roo-card-rotated');
2389             this.fireEvent('rotate', this, true);
2390         }
2391         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2392         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2393         
2394     },
2395     getDragData : function(e)
2396     {
2397         var target = this.getEl();
2398         if (target) {
2399             //this.handleSelection(e);
2400             
2401             var dragData = {
2402                 source: this,
2403                 copy: false,
2404                 nodes: this.getEl(),
2405                 records: []
2406             };
2407             
2408             
2409             dragData.ddel = target.dom ;    // the div element
2410             Roo.log(target.getWidth( ));
2411             dragData.ddel.style.width = target.getWidth() + 'px';
2412             
2413             return dragData;
2414         }
2415         return false;
2416     },
2417     /**
2418     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2419     *    whole Element becomes the target, and this causes the drop gesture to append.
2420     *
2421     *    Returns an object:
2422     *     {
2423            
2424            position : 'below' or 'above'
2425            card  : relateive to card OBJECT (or true for no cards listed)
2426            items_n : relative to nth item in list
2427            card_n : relative to  nth card in list
2428     }
2429     *
2430     *    
2431     */
2432     getTargetFromEvent : function(e, dragged_card_el)
2433     {
2434         var target = e.getTarget();
2435         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2436             target = target.parentNode;
2437         }
2438         
2439         var ret = {
2440             position: '',
2441             cards : [],
2442             card_n : -1,
2443             items_n : -1,
2444             card : false 
2445         };
2446         
2447         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2448         // see if target is one of the 'cards'...
2449         
2450         
2451         //Roo.log(this.items.length);
2452         var pos = false;
2453         
2454         var last_card_n = 0;
2455         var cards_len  = 0;
2456         for (var i = 0;i< this.items.length;i++) {
2457             
2458             if (!this.items[i].el.hasClass('card')) {
2459                  continue;
2460             }
2461             pos = this.getDropPoint(e, this.items[i].el.dom);
2462             
2463             cards_len = ret.cards.length;
2464             //Roo.log(this.items[i].el.dom.id);
2465             ret.cards.push(this.items[i]);
2466             last_card_n  = i;
2467             if (ret.card_n < 0 && pos == 'above') {
2468                 ret.position = cards_len > 0 ? 'below' : pos;
2469                 ret.items_n = i > 0 ? i - 1 : 0;
2470                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2471                 ret.card = ret.cards[ret.card_n];
2472             }
2473         }
2474         if (!ret.cards.length) {
2475             ret.card = true;
2476             ret.position = 'below';
2477             ret.items_n;
2478             return ret;
2479         }
2480         // could not find a card.. stick it at the end..
2481         if (ret.card_n < 0) {
2482             ret.card_n = last_card_n;
2483             ret.card = ret.cards[last_card_n];
2484             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2485             ret.position = 'below';
2486         }
2487         
2488         if (this.items[ret.items_n].el == dragged_card_el) {
2489             return false;
2490         }
2491         
2492         if (ret.position == 'below') {
2493             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2494             
2495             if (card_after  && card_after.el == dragged_card_el) {
2496                 return false;
2497             }
2498             return ret;
2499         }
2500         
2501         // its's after ..
2502         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2503         
2504         if (card_before  && card_before.el == dragged_card_el) {
2505             return false;
2506         }
2507         
2508         return ret;
2509     },
2510     
2511     onNodeEnter : function(n, dd, e, data){
2512         return false;
2513     },
2514     onNodeOver : function(n, dd, e, data)
2515     {
2516        
2517         var target_info = this.getTargetFromEvent(e,data.source.el);
2518         if (target_info === false) {
2519             this.dropPlaceHolder('hide');
2520             return false;
2521         }
2522         Roo.log(['getTargetFromEvent', target_info ]);
2523         
2524         
2525         if (this.fireEvent('cardover', this, [ data ]) === false) {
2526             return false;
2527         }
2528         
2529         this.dropPlaceHolder('show', target_info,data);
2530         
2531         return false; 
2532     },
2533     onNodeOut : function(n, dd, e, data){
2534         this.dropPlaceHolder('hide');
2535      
2536     },
2537     onNodeDrop : function(n, dd, e, data)
2538     {
2539         
2540         // call drop - return false if
2541         
2542         // this could actually fail - if the Network drops..
2543         // we will ignore this at present..- client should probably reload
2544         // the whole set of cards if stuff like that fails.
2545         
2546         
2547         var info = this.getTargetFromEvent(e,data.source.el);
2548         if (info === false) {
2549             return false;
2550         }
2551         this.dropPlaceHolder('hide');
2552   
2553           
2554     
2555         this.acceptCard(data.source, info.position, info.card, info.items_n);
2556         return true;
2557          
2558     },
2559     firstChildCard : function()
2560     {
2561         for (var i = 0;i< this.items.length;i++) {
2562             
2563             if (!this.items[i].el.hasClass('card')) {
2564                  continue;
2565             }
2566             return this.items[i];
2567         }
2568         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2569     },
2570     /**
2571      * accept card
2572      *
2573      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2574      */
2575     acceptCard : function(move_card,  position, next_to_card )
2576     {
2577         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2578             return false;
2579         }
2580         
2581         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2582         
2583         move_card.parent().removeCard(move_card);
2584         
2585         
2586         var dom = move_card.el.dom;
2587         dom.style.width = ''; // clear with - which is set by drag.
2588         
2589         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2590             var cardel = next_to_card.el.dom;
2591             
2592             if (position == 'above' ) {
2593                 cardel.parentNode.insertBefore(dom, cardel);
2594             } else if (cardel.nextSibling) {
2595                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2596             } else {
2597                 cardel.parentNode.append(dom);
2598             }
2599         } else {
2600             // card container???
2601             this.containerEl.dom.append(dom);
2602         }
2603         
2604         //FIXME HANDLE card = true 
2605         
2606         // add this to the correct place in items.
2607         
2608         // remove Card from items.
2609         
2610        
2611         if (this.items.length) {
2612             var nitems = [];
2613             //Roo.log([info.items_n, info.position, this.items.length]);
2614             for (var i =0; i < this.items.length; i++) {
2615                 if (i == to_items_n && position == 'above') {
2616                     nitems.push(move_card);
2617                 }
2618                 nitems.push(this.items[i]);
2619                 if (i == to_items_n && position == 'below') {
2620                     nitems.push(move_card);
2621                 }
2622             }
2623             this.items = nitems;
2624             Roo.log(this.items);
2625         } else {
2626             this.items.push(move_card);
2627         }
2628         
2629         move_card.parentId = this.id;
2630         
2631         return true;
2632         
2633         
2634     },
2635     removeCard : function(c)
2636     {
2637         this.items = this.items.filter(function(e) { return e != c });
2638  
2639         var dom = c.el.dom;
2640         dom.parentNode.removeChild(dom);
2641         dom.style.width = ''; // clear with - which is set by drag.
2642         c.parentId = false;
2643         
2644     },
2645     
2646     /**    Decide whether to drop above or below a View node. */
2647     getDropPoint : function(e, n, dd)
2648     {
2649         if (dd) {
2650              return false;
2651         }
2652         if (n == this.containerEl.dom) {
2653             return "above";
2654         }
2655         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2656         var c = t + (b - t) / 2;
2657         var y = Roo.lib.Event.getPageY(e);
2658         if(y <= c) {
2659             return "above";
2660         }else{
2661             return "below";
2662         }
2663     },
2664     onToggleCollapse : function(e)
2665         {
2666         if (this.collapsed) {
2667             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2668             this.collapsableEl.addClass('show');
2669             this.collapsed = false;
2670             return;
2671         }
2672         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2673         this.collapsableEl.removeClass('show');
2674         this.collapsed = true;
2675         
2676     
2677     },
2678     
2679     onToggleRotate : function(e)
2680     {
2681         this.collapsableEl.removeClass('show');
2682         this.footerEl.removeClass('d-none');
2683         this.el.removeClass('roo-card-rotated');
2684         this.el.removeClass('d-none');
2685         if (this.rotated) {
2686             
2687             this.collapsableEl.addClass('show');
2688             this.rotated = false;
2689             this.fireEvent('rotate', this, this.rotated);
2690             return;
2691         }
2692         this.el.addClass('roo-card-rotated');
2693         this.footerEl.addClass('d-none');
2694         this.el.select('.roo-collapsable').removeClass('show');
2695         
2696         this.rotated = true;
2697         this.fireEvent('rotate', this, this.rotated);
2698     
2699     },
2700     
2701     dropPlaceHolder: function (action, info, data)
2702     {
2703         if (this.dropEl === false) {
2704             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2705             cls : 'd-none'
2706             },true);
2707         }
2708         this.dropEl.removeClass(['d-none', 'd-block']);        
2709         if (action == 'hide') {
2710             
2711             this.dropEl.addClass('d-none');
2712             return;
2713         }
2714         // FIXME - info.card == true!!!
2715         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2716         
2717         if (info.card !== true) {
2718             var cardel = info.card.el.dom;
2719             
2720             if (info.position == 'above') {
2721                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2722             } else if (cardel.nextSibling) {
2723                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2724             } else {
2725                 cardel.parentNode.append(this.dropEl.dom);
2726             }
2727         } else {
2728             // card container???
2729             this.containerEl.dom.append(this.dropEl.dom);
2730         }
2731         
2732         this.dropEl.addClass('d-block roo-card-dropzone');
2733         
2734         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2735         
2736         
2737     
2738     
2739     
2740     },
2741     setHeaderText: function(html)
2742     {
2743         this.header = html;
2744         if (this.headerContainerEl) {
2745             this.headerContainerEl.dom.innerHTML = html;
2746         }
2747     },
2748     onHeaderImageLoad : function(ev, he)
2749     {
2750         if (!this.header_image_fit_square) {
2751             return;
2752         }
2753         
2754         var hw = he.naturalHeight / he.naturalWidth;
2755         // wide image = < 0
2756         // tall image = > 1
2757         //var w = he.dom.naturalWidth;
2758         var ww = he.width;
2759         he.style.left =  0;
2760         he.style.position =  'relative';
2761         if (hw > 1) {
2762             var nw = (ww * (1/hw));
2763             Roo.get(he).setSize( ww * (1/hw),  ww);
2764             he.style.left =  ((ww - nw)/ 2) + 'px';
2765             he.style.position =  'relative';
2766         }
2767
2768     }
2769
2770     
2771 });
2772
2773 /*
2774  * - LGPL
2775  *
2776  * Card header - holder for the card header elements.
2777  * 
2778  */
2779
2780 /**
2781  * @class Roo.bootstrap.CardHeader
2782  * @extends Roo.bootstrap.Element
2783  * @parent Roo.bootstrap.Card
2784  * @children Roo.bootstrap.Component
2785  * Bootstrap CardHeader class
2786  * @constructor
2787  * Create a new Card Header - that you can embed children into
2788  * @param {Object} config The config object
2789  */
2790
2791 Roo.bootstrap.CardHeader = function(config){
2792     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2793 };
2794
2795 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2796     
2797     
2798     container_method : 'getCardHeader' 
2799     
2800      
2801     
2802     
2803    
2804 });
2805
2806  
2807
2808  /*
2809  * - LGPL
2810  *
2811  * Card footer - holder for the card footer elements.
2812  * 
2813  */
2814
2815 /**
2816  * @class Roo.bootstrap.CardFooter
2817  * @extends Roo.bootstrap.Element
2818  * @parent Roo.bootstrap.Card
2819  * @children Roo.bootstrap.Component
2820  * Bootstrap CardFooter class
2821  * 
2822  * @constructor
2823  * Create a new Card Footer - that you can embed children into
2824  * @param {Object} config The config object
2825  */
2826
2827 Roo.bootstrap.CardFooter = function(config){
2828     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2829 };
2830
2831 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2832     
2833     
2834     container_method : 'getCardFooter' 
2835     
2836      
2837     
2838     
2839    
2840 });
2841
2842  
2843
2844  /*
2845  * - LGPL
2846  *
2847  * Card header - holder for the card header elements.
2848  * 
2849  */
2850
2851 /**
2852  * @class Roo.bootstrap.CardImageTop
2853  * @extends Roo.bootstrap.Element
2854  * @parent Roo.bootstrap.Card
2855  * @children Roo.bootstrap.Component
2856  * Bootstrap CardImageTop class
2857  * 
2858  * @constructor
2859  * Create a new Card Image Top container
2860  * @param {Object} config The config object
2861  */
2862
2863 Roo.bootstrap.CardImageTop = function(config){
2864     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2865 };
2866
2867 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2868     
2869    
2870     container_method : 'getCardImageTop' 
2871     
2872      
2873     
2874    
2875 });
2876
2877  
2878
2879  
2880 /*
2881 * Licence: LGPL
2882 */
2883
2884 /**
2885  * @class Roo.bootstrap.ButtonUploader
2886  * @extends Roo.bootstrap.Button
2887  * Bootstrap Button Uploader class - it's a button which when you add files to it
2888  *
2889  * 
2890  * @cfg {Number} errorTimeout default 3000
2891  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2892  * @cfg {Array}  html The button text.
2893  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2894  *
2895  * @constructor
2896  * Create a new CardUploader
2897  * @param {Object} config The config object
2898  */
2899
2900 Roo.bootstrap.ButtonUploader = function(config){
2901     
2902  
2903     
2904     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2905     
2906      
2907      this.addEvents({
2908          // raw events
2909         /**
2910          * @event beforeselect
2911          * When button is pressed, before show upload files dialog is shown
2912          * @param {Roo.bootstrap.UploaderButton} this
2913          *
2914          */
2915         'beforeselect' : true,
2916          /**
2917          * @event fired when files have been selected, 
2918          * When a the download link is clicked
2919          * @param {Roo.bootstrap.UploaderButton} this
2920          * @param {Array} Array of files that have been uploaded
2921          */
2922         'uploaded' : true
2923         
2924     });
2925 };
2926  
2927 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2928     
2929      
2930     errorTimeout : 3000,
2931      
2932     images : false,
2933    
2934     fileCollection : false,
2935     allowBlank : true,
2936     
2937     multiple : true,
2938     
2939     getAutoCreate : function()
2940     {
2941        
2942         
2943         return  {
2944             cls :'div' ,
2945             cn : [
2946                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2947             ]
2948         };
2949            
2950          
2951     },
2952      
2953    
2954     initEvents : function()
2955     {
2956         
2957         Roo.bootstrap.Button.prototype.initEvents.call(this);
2958         
2959         
2960         
2961         
2962         
2963         this.urlAPI = (window.createObjectURL && window) || 
2964                                 (window.URL && URL.revokeObjectURL && URL) || 
2965                                 (window.webkitURL && webkitURL);
2966                         
2967         var im = {
2968             tag: 'input',
2969             type : 'file',
2970             cls : 'd-none  roo-card-upload-selector' 
2971           
2972         };
2973         if (this.multiple) {
2974             im.multiple = 'multiple';
2975         }
2976         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2977        
2978         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2979         
2980         this.selectorEl.on('change', this.onFileSelected, this);
2981          
2982          
2983        
2984     },
2985     
2986    
2987     onClick : function(e)
2988     {
2989         e.preventDefault();
2990         
2991         if ( this.fireEvent('beforeselect', this) === false) {
2992             return;
2993         }
2994          
2995         this.selectorEl.dom.click();
2996          
2997     },
2998     
2999     onFileSelected : function(e)
3000     {
3001         e.preventDefault();
3002         
3003         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3004             return;
3005         }
3006         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3007         this.selectorEl.dom.value  = '';// hopefully reset..
3008         
3009         this.fireEvent('uploaded', this,  files );
3010         
3011     },
3012     
3013        
3014    
3015     
3016     /**
3017      * addCard - add an Attachment to the uploader
3018      * @param data - the data about the image to upload
3019      *
3020      * {
3021           id : 123
3022           title : "Title of file",
3023           is_uploaded : false,
3024           src : "http://.....",
3025           srcfile : { the File upload object },
3026           mimetype : file.type,
3027           preview : false,
3028           is_deleted : 0
3029           .. any other data...
3030         }
3031      *
3032      * 
3033     */
3034      
3035     reset: function()
3036     {
3037          
3038          this.selectorEl
3039     } 
3040     
3041     
3042     
3043     
3044 });
3045  /*
3046  * - LGPL
3047  *
3048  * image
3049  * 
3050  */
3051
3052
3053 /**
3054  * @class Roo.bootstrap.Img
3055  * @extends Roo.bootstrap.Component
3056  * Bootstrap Img class
3057  * @cfg {Boolean} imgResponsive false | true
3058  * @cfg {String} border rounded | circle | thumbnail
3059  * @cfg {String} src image source
3060  * @cfg {String} alt image alternative text
3061  * @cfg {String} href a tag href
3062  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3063  * @cfg {String} xsUrl xs image source
3064  * @cfg {String} smUrl sm image source
3065  * @cfg {String} mdUrl md image source
3066  * @cfg {String} lgUrl lg image source
3067  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3068  * 
3069  * @constructor
3070  * Create a new Input
3071  * @param {Object} config The config object
3072  */
3073
3074 Roo.bootstrap.Img = function(config){
3075     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3076     
3077     this.addEvents({
3078         // img events
3079         /**
3080          * @event click
3081          * The img click event for the img.
3082          * @param {Roo.EventObject} e
3083          */
3084         "click" : true,
3085         /**
3086          * @event load
3087          * The when any image loads
3088          * @param {Roo.EventObject} e
3089          */
3090         "load" : true
3091     });
3092 };
3093
3094 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3095     
3096     imgResponsive: true,
3097     border: '',
3098     src: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=',
3099     href: false,
3100     target: false,
3101     xsUrl: '',
3102     smUrl: '',
3103     mdUrl: '',
3104     lgUrl: '',
3105     backgroundContain : false,
3106
3107     getAutoCreate : function()
3108     {   
3109         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3110             return this.createSingleImg();
3111         }
3112         
3113         var cfg = {
3114             tag: 'div',
3115             cls: 'roo-image-responsive-group',
3116             cn: []
3117         };
3118         var _this = this;
3119         
3120         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3121             
3122             if(!_this[size + 'Url']){
3123                 return;
3124             }
3125             
3126             var img = {
3127                 tag: 'img',
3128                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3129                 html: _this.html || cfg.html,
3130                 src: _this[size + 'Url']
3131             };
3132             
3133             img.cls += ' roo-image-responsive-' + size;
3134             
3135             var s = ['xs', 'sm', 'md', 'lg'];
3136             
3137             s.splice(s.indexOf(size), 1);
3138             
3139             Roo.each(s, function(ss){
3140                 img.cls += ' hidden-' + ss;
3141             });
3142             
3143             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3144                 cfg.cls += ' img-' + _this.border;
3145             }
3146             
3147             if(_this.alt){
3148                 cfg.alt = _this.alt;
3149             }
3150             
3151             if(_this.href){
3152                 var a = {
3153                     tag: 'a',
3154                     href: _this.href,
3155                     cn: [
3156                         img
3157                     ]
3158                 };
3159
3160                 if(this.target){
3161                     a.target = _this.target;
3162                 }
3163             }
3164             
3165             cfg.cn.push((_this.href) ? a : img);
3166             
3167         });
3168         
3169         return cfg;
3170     },
3171     
3172     createSingleImg : function()
3173     {
3174         var cfg = {
3175             tag: 'img',
3176             cls: (this.imgResponsive) ? 'img-responsive' : '',
3177             html : null,
3178             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3179         };
3180         
3181         if (this.backgroundContain) {
3182             cfg.cls += ' background-contain';
3183         }
3184         
3185         cfg.html = this.html || cfg.html;
3186         
3187         if (this.backgroundContain) {
3188             cfg.style="background-image: url(" + this.src + ')';
3189         } else {
3190             cfg.src = this.src || cfg.src;
3191         }
3192         
3193         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3194             cfg.cls += ' img-' + this.border;
3195         }
3196         
3197         if(this.alt){
3198             cfg.alt = this.alt;
3199         }
3200         
3201         if(this.href){
3202             var a = {
3203                 tag: 'a',
3204                 href: this.href,
3205                 cn: [
3206                     cfg
3207                 ]
3208             };
3209             
3210             if(this.target){
3211                 a.target = this.target;
3212             }
3213             
3214         }
3215         
3216         return (this.href) ? a : cfg;
3217     },
3218     
3219     initEvents: function() 
3220     {
3221         if(!this.href){
3222             this.el.on('click', this.onClick, this);
3223         }
3224         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3225             this.el.on('load', this.onImageLoad, this);
3226         } else {
3227             // not sure if this works.. not tested
3228             this.el.select('img', true).on('load', this.onImageLoad, this);
3229         }
3230         
3231     },
3232     
3233     onClick : function(e)
3234     {
3235         Roo.log('img onclick');
3236         this.fireEvent('click', this, e);
3237     },
3238     onImageLoad: function(e)
3239     {
3240         Roo.log('img load');
3241         this.fireEvent('load', this, e);
3242     },
3243     
3244     /**
3245      * Sets the url of the image - used to update it
3246      * @param {String} url the url of the image
3247      */
3248     
3249     setSrc : function(url)
3250     {
3251         this.src =  url;
3252         
3253         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3254             if (this.backgroundContain) {
3255                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3256             } else {
3257                 this.el.dom.src =  url;
3258             }
3259             return;
3260         }
3261         
3262         this.el.select('img', true).first().dom.src =  url;
3263     }
3264     
3265     
3266    
3267 });
3268
3269  /*
3270  * - LGPL
3271  *
3272  * image
3273  * 
3274  */
3275
3276
3277 /**
3278  * @class Roo.bootstrap.Link
3279  * @extends Roo.bootstrap.Component
3280  * @children Roo.bootstrap.Component
3281  * Bootstrap Link Class (eg. '<a href>')
3282  
3283  * @cfg {String} alt image alternative text
3284  * @cfg {String} href a tag href
3285  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3286  * @cfg {String} html the content of the link.
3287  * @cfg {String} anchor name for the anchor link
3288  * @cfg {String} fa - favicon
3289
3290  * @cfg {Boolean} preventDefault (true | false) default false
3291
3292  * 
3293  * @constructor
3294  * Create a new Input
3295  * @param {Object} config The config object
3296  */
3297
3298 Roo.bootstrap.Link = function(config){
3299     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3300     
3301     this.addEvents({
3302         // img events
3303         /**
3304          * @event click
3305          * The img click event for the img.
3306          * @param {Roo.EventObject} e
3307          */
3308         "click" : true
3309     });
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3313     
3314     href: false,
3315     target: false,
3316     preventDefault: false,
3317     anchor : false,
3318     alt : false,
3319     fa: false,
3320
3321
3322     getAutoCreate : function()
3323     {
3324         var html = this.html || '';
3325         
3326         if (this.fa !== false) {
3327             html = '<i class="fa fa-' + this.fa + '"></i>';
3328         }
3329         var cfg = {
3330             tag: 'a'
3331         };
3332         // anchor's do not require html/href...
3333         if (this.anchor === false) {
3334             cfg.html = html;
3335             cfg.href = this.href || '#';
3336         } else {
3337             cfg.name = this.anchor;
3338             if (this.html !== false || this.fa !== false) {
3339                 cfg.html = html;
3340             }
3341             if (this.href !== false) {
3342                 cfg.href = this.href;
3343             }
3344         }
3345         
3346         if(this.alt !== false){
3347             cfg.alt = this.alt;
3348         }
3349         
3350         
3351         if(this.target !== false) {
3352             cfg.target = this.target;
3353         }
3354         
3355         return cfg;
3356     },
3357     
3358     initEvents: function() {
3359         
3360         if(!this.href || this.preventDefault){
3361             this.el.on('click', this.onClick, this);
3362         }
3363     },
3364     
3365     onClick : function(e)
3366     {
3367         if(this.preventDefault){
3368             e.preventDefault();
3369         }
3370         //Roo.log('img onclick');
3371         this.fireEvent('click', this, e);
3372     }
3373    
3374 });
3375
3376  /*
3377  * - LGPL
3378  *
3379  * header
3380  * 
3381  */
3382
3383 /**
3384  * @class Roo.bootstrap.Header
3385  * @extends Roo.bootstrap.Component
3386  * @children Roo.bootstrap.Component
3387  * Bootstrap Header class
3388  *
3389  * 
3390  * @cfg {String} html content of header
3391  * @cfg {Number} level (1|2|3|4|5|6) default 1
3392  * 
3393  * @constructor
3394  * Create a new Header
3395  * @param {Object} config The config object
3396  */
3397
3398
3399 Roo.bootstrap.Header  = function(config){
3400     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3401 };
3402
3403 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3404     
3405     //href : false,
3406     html : false,
3407     level : 1,
3408     
3409     
3410     
3411     getAutoCreate : function(){
3412         
3413         
3414         
3415         var cfg = {
3416             tag: 'h' + (1 *this.level),
3417             html: this.html || ''
3418         } ;
3419         
3420         return cfg;
3421     }
3422    
3423 });
3424
3425  
3426
3427  /**
3428  * @class Roo.bootstrap.MenuMgr
3429  * @licence LGPL
3430  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3431  * @static
3432  */
3433 Roo.bootstrap.menu.Manager = function(){
3434    var menus, active, groups = {}, attached = false, lastShow = new Date();
3435
3436    // private - called when first menu is created
3437    function init(){
3438        menus = {};
3439        active = new Roo.util.MixedCollection();
3440        Roo.get(document).addKeyListener(27, function(){
3441            if(active.length > 0){
3442                hideAll();
3443            }
3444        });
3445    }
3446
3447    // private
3448    function hideAll(){
3449        if(active && active.length > 0){
3450            var c = active.clone();
3451            c.each(function(m){
3452                m.hide();
3453            });
3454        }
3455    }
3456
3457    // private
3458    function onHide(m){
3459        active.remove(m);
3460        if(active.length < 1){
3461            Roo.get(document).un("mouseup", onMouseDown);
3462             
3463            attached = false;
3464        }
3465    }
3466
3467    // private
3468    function onShow(m){
3469        var last = active.last();
3470        lastShow = new Date();
3471        active.add(m);
3472        if(!attached){
3473           Roo.get(document).on("mouseup", onMouseDown);
3474            
3475            attached = true;
3476        }
3477        if(m.parentMenu){
3478           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3479           m.parentMenu.activeChild = m;
3480        }else if(last && last.isVisible()){
3481           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3482        }
3483    }
3484
3485    // private
3486    function onBeforeHide(m){
3487        if(m.activeChild){
3488            m.activeChild.hide();
3489        }
3490        if(m.autoHideTimer){
3491            clearTimeout(m.autoHideTimer);
3492            delete m.autoHideTimer;
3493        }
3494    }
3495
3496    // private
3497    function onBeforeShow(m){
3498        var pm = m.parentMenu;
3499        if(!pm && !m.allowOtherMenus){
3500            hideAll();
3501        }else if(pm && pm.activeChild && active != m){
3502            pm.activeChild.hide();
3503        }
3504    }
3505
3506    // private this should really trigger on mouseup..
3507    function onMouseDown(e){
3508         Roo.log("on Mouse Up");
3509         
3510         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3511             Roo.log("MenuManager hideAll");
3512             hideAll();
3513             e.stopEvent();
3514         }
3515         
3516         
3517    }
3518
3519    // private
3520    function onBeforeCheck(mi, state){
3521        if(state){
3522            var g = groups[mi.group];
3523            for(var i = 0, l = g.length; i < l; i++){
3524                if(g[i] != mi){
3525                    g[i].setChecked(false);
3526                }
3527            }
3528        }
3529    }
3530
3531    return {
3532
3533        /**
3534         * Hides all menus that are currently visible
3535         */
3536        hideAll : function(){
3537             hideAll();  
3538        },
3539
3540        // private
3541        register : function(menu){
3542            if(!menus){
3543                init();
3544            }
3545            menus[menu.id] = menu;
3546            menu.on("beforehide", onBeforeHide);
3547            menu.on("hide", onHide);
3548            menu.on("beforeshow", onBeforeShow);
3549            menu.on("show", onShow);
3550            var g = menu.group;
3551            if(g && menu.events["checkchange"]){
3552                if(!groups[g]){
3553                    groups[g] = [];
3554                }
3555                groups[g].push(menu);
3556                menu.on("checkchange", onCheck);
3557            }
3558        },
3559
3560         /**
3561          * Returns a {@link Roo.menu.Menu} object
3562          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3563          * be used to generate and return a new Menu instance.
3564          */
3565        get : function(menu){
3566            if(typeof menu == "string"){ // menu id
3567                return menus[menu];
3568            }else if(menu.events){  // menu instance
3569                return menu;
3570            }
3571            /*else if(typeof menu.length == 'number'){ // array of menu items?
3572                return new Roo.bootstrap.Menu({items:menu});
3573            }else{ // otherwise, must be a config
3574                return new Roo.bootstrap.Menu(menu);
3575            }
3576            */
3577            return false;
3578        },
3579
3580        // private
3581        unregister : function(menu){
3582            delete menus[menu.id];
3583            menu.un("beforehide", onBeforeHide);
3584            menu.un("hide", onHide);
3585            menu.un("beforeshow", onBeforeShow);
3586            menu.un("show", onShow);
3587            var g = menu.group;
3588            if(g && menu.events["checkchange"]){
3589                groups[g].remove(menu);
3590                menu.un("checkchange", onCheck);
3591            }
3592        },
3593
3594        // private
3595        registerCheckable : function(menuItem){
3596            var g = menuItem.group;
3597            if(g){
3598                if(!groups[g]){
3599                    groups[g] = [];
3600                }
3601                groups[g].push(menuItem);
3602                menuItem.on("beforecheckchange", onBeforeCheck);
3603            }
3604        },
3605
3606        // private
3607        unregisterCheckable : function(menuItem){
3608            var g = menuItem.group;
3609            if(g){
3610                groups[g].remove(menuItem);
3611                menuItem.un("beforecheckchange", onBeforeCheck);
3612            }
3613        }
3614    };
3615 }(); 
3616 /**
3617  * @class Roo.bootstrap.menu.Menu
3618  * @extends Roo.bootstrap.Component
3619  * @licence LGPL
3620  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3621  * @parent none
3622  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3623  * 
3624  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3625  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3626  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3627  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3628 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3629 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3630  
3631  * @constructor
3632  * Create a new Menu
3633  * @param {Object} config The config objectQ
3634  */
3635
3636
3637 Roo.bootstrap.menu.Menu = function(config){
3638     
3639     if (config.type == 'treeview') {
3640         // normally menu's are drawn attached to the document to handle layering etc..
3641         // however treeview (used by the docs menu is drawn into the parent element)
3642         this.container_method = 'getChildContainer'; 
3643     }
3644     
3645     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3646     if (this.registerMenu && this.type != 'treeview')  {
3647         Roo.bootstrap.menu.Manager.register(this);
3648     }
3649     
3650     
3651     this.addEvents({
3652         /**
3653          * @event beforeshow
3654          * Fires before this menu is displayed (return false to block)
3655          * @param {Roo.menu.Menu} this
3656          */
3657         beforeshow : true,
3658         /**
3659          * @event beforehide
3660          * Fires before this menu is hidden (return false to block)
3661          * @param {Roo.menu.Menu} this
3662          */
3663         beforehide : true,
3664         /**
3665          * @event show
3666          * Fires after this menu is displayed
3667          * @param {Roo.menu.Menu} this
3668          */
3669         show : true,
3670         /**
3671          * @event hide
3672          * Fires after this menu is hidden
3673          * @param {Roo.menu.Menu} this
3674          */
3675         hide : true,
3676         /**
3677          * @event click
3678          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3679          * @param {Roo.menu.Menu} this
3680          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681          * @param {Roo.EventObject} e
3682          */
3683         click : true,
3684         /**
3685          * @event mouseover
3686          * Fires when the mouse is hovering over this menu
3687          * @param {Roo.menu.Menu} this
3688          * @param {Roo.EventObject} e
3689          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3690          */
3691         mouseover : true,
3692         /**
3693          * @event mouseout
3694          * Fires when the mouse exits this menu
3695          * @param {Roo.menu.Menu} this
3696          * @param {Roo.EventObject} e
3697          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3698          */
3699         mouseout : true,
3700         /**
3701          * @event itemclick
3702          * Fires when a menu item contained in this menu is clicked
3703          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3704          * @param {Roo.EventObject} e
3705          */
3706         itemclick: true
3707     });
3708     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3709 };
3710
3711 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3712     
3713    /// html : false,
3714    
3715     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3716     type: false,
3717     /**
3718      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3719      */
3720     registerMenu : true,
3721     
3722     menuItems :false, // stores the menu items..
3723     
3724     hidden:true,
3725         
3726     parentMenu : false,
3727     
3728     stopEvent : true,
3729     
3730     isLink : false,
3731     
3732     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3733     
3734     hideTrigger : false,
3735     
3736     align : 'tl-bl?',
3737     
3738     
3739     getChildContainer : function() {
3740         return this.el;  
3741     },
3742     
3743     getAutoCreate : function(){
3744          
3745         //if (['right'].indexOf(this.align)!==-1) {
3746         //    cfg.cn[1].cls += ' pull-right'
3747         //}
3748          
3749         var cfg = {
3750             tag : 'ul',
3751             cls : 'dropdown-menu shadow' ,
3752             style : 'z-index:1000'
3753             
3754         };
3755         
3756         if (this.type === 'submenu') {
3757             cfg.cls = 'submenu active';
3758         }
3759         if (this.type === 'treeview') {
3760             cfg.cls = 'treeview-menu';
3761         }
3762         
3763         return cfg;
3764     },
3765     initEvents : function() {
3766         
3767        // Roo.log("ADD event");
3768        // Roo.log(this.triggerEl.dom);
3769         if (this.triggerEl) {
3770             
3771             this.triggerEl.on('click', this.onTriggerClick, this);
3772             
3773             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3774             
3775             if (!this.hideTrigger) {
3776                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3777                     // dropdown toggle on the 'a' in BS4?
3778                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3779                 } else {
3780                     this.triggerEl.addClass('dropdown-toggle');
3781                 }
3782             }
3783         }
3784         
3785         if (Roo.isTouch) {
3786             this.el.on('touchstart'  , this.onTouch, this);
3787         }
3788         this.el.on('click' , this.onClick, this);
3789
3790         this.el.on("mouseover", this.onMouseOver, this);
3791         this.el.on("mouseout", this.onMouseOut, this);
3792         
3793     },
3794     
3795     findTargetItem : function(e)
3796     {
3797         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3798         if(!t){
3799             return false;
3800         }
3801         //Roo.log(t);         Roo.log(t.id);
3802         if(t && t.id){
3803             //Roo.log(this.menuitems);
3804             return this.menuitems.get(t.id);
3805             
3806             //return this.items.get(t.menuItemId);
3807         }
3808         
3809         return false;
3810     },
3811     
3812     onTouch : function(e) 
3813     {
3814         Roo.log("menu.onTouch");
3815         //e.stopEvent(); this make the user popdown broken
3816         this.onClick(e);
3817     },
3818     
3819     onClick : function(e)
3820     {
3821         Roo.log("menu.onClick");
3822         
3823         var t = this.findTargetItem(e);
3824         if(!t || t.isContainer){
3825             return;
3826         }
3827         Roo.log(e);
3828         /*
3829         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3830             if(t == this.activeItem && t.shouldDeactivate(e)){
3831                 this.activeItem.deactivate();
3832                 delete this.activeItem;
3833                 return;
3834             }
3835             if(t.canActivate){
3836                 this.setActiveItem(t, true);
3837             }
3838             return;
3839             
3840             
3841         }
3842         */
3843        
3844         Roo.log('pass click event');
3845         
3846         t.onClick(e);
3847         
3848         this.fireEvent("click", this, t, e);
3849         
3850         var _this = this;
3851         
3852         if(!t.href.length || t.href == '#'){
3853             (function() { _this.hide(); }).defer(100);
3854         }
3855         
3856     },
3857     
3858     onMouseOver : function(e){
3859         var t  = this.findTargetItem(e);
3860         //Roo.log(t);
3861         //if(t){
3862         //    if(t.canActivate && !t.disabled){
3863         //        this.setActiveItem(t, true);
3864         //    }
3865         //}
3866         
3867         this.fireEvent("mouseover", this, e, t);
3868     },
3869     isVisible : function(){
3870         return !this.hidden;
3871     },
3872     onMouseOut : function(e){
3873         var t  = this.findTargetItem(e);
3874         
3875         //if(t ){
3876         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3877         //        this.activeItem.deactivate();
3878         //        delete this.activeItem;
3879         //    }
3880         //}
3881         this.fireEvent("mouseout", this, e, t);
3882     },
3883     
3884     
3885     /**
3886      * Displays this menu relative to another element
3887      * @param {String/HTMLElement/Roo.Element} element The element to align to
3888      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3889      * the element (defaults to this.defaultAlign)
3890      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3891      */
3892     show : function(el, pos, parentMenu)
3893     {
3894         if (false === this.fireEvent("beforeshow", this)) {
3895             Roo.log("show canceled");
3896             return;
3897         }
3898         this.parentMenu = parentMenu;
3899         if(!this.el){
3900             this.render();
3901         }
3902         this.el.addClass('show'); // show otherwise we do not know how big we are..
3903          
3904         var xy = this.el.getAlignToXY(el, pos);
3905         
3906         // bl-tl << left align  below
3907         // tl-bl << left align 
3908         
3909         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3910             // if it goes to far to the right.. -> align left.
3911             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3912         }
3913         if(xy[0] < 0){
3914             // was left align - go right?
3915             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3916         }
3917         
3918         // goes down the bottom
3919         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3920            xy[1]  < 0 ){
3921             var a = this.align.replace('?', '').split('-');
3922             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3923             
3924         }
3925         
3926         this.showAt(  xy , parentMenu, false);
3927     },
3928      /**
3929      * Displays this menu at a specific xy position
3930      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3931      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3932      */
3933     showAt : function(xy, parentMenu, /* private: */_e){
3934         this.parentMenu = parentMenu;
3935         if(!this.el){
3936             this.render();
3937         }
3938         if(_e !== false){
3939             this.fireEvent("beforeshow", this);
3940             //xy = this.el.adjustForConstraints(xy);
3941         }
3942         
3943         //this.el.show();
3944         this.hideMenuItems();
3945         this.hidden = false;
3946         if (this.triggerEl) {
3947             this.triggerEl.addClass('open');
3948         }
3949         
3950         this.el.addClass('show');
3951         
3952         
3953         
3954         // reassign x when hitting right
3955         
3956         // reassign y when hitting bottom
3957         
3958         // but the list may align on trigger left or trigger top... should it be a properity?
3959         
3960         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3961             this.el.setXY(xy);
3962         }
3963         
3964         this.focus();
3965         this.fireEvent("show", this);
3966     },
3967     
3968     focus : function(){
3969         return;
3970         if(!this.hidden){
3971             this.doFocus.defer(50, this);
3972         }
3973     },
3974
3975     doFocus : function(){
3976         if(!this.hidden){
3977             this.focusEl.focus();
3978         }
3979     },
3980
3981     /**
3982      * Hides this menu and optionally all parent menus
3983      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3984      */
3985     hide : function(deep)
3986     {
3987         if (false === this.fireEvent("beforehide", this)) {
3988             Roo.log("hide canceled");
3989             return;
3990         }
3991         this.hideMenuItems();
3992         if(this.el && this.isVisible()){
3993            
3994             if(this.activeItem){
3995                 this.activeItem.deactivate();
3996                 this.activeItem = null;
3997             }
3998             if (this.triggerEl) {
3999                 this.triggerEl.removeClass('open');
4000             }
4001             
4002             this.el.removeClass('show');
4003             this.hidden = true;
4004             this.fireEvent("hide", this);
4005         }
4006         if(deep === true && this.parentMenu){
4007             this.parentMenu.hide(true);
4008         }
4009     },
4010     
4011     onTriggerClick : function(e)
4012     {
4013         Roo.log('trigger click');
4014         
4015         var target = e.getTarget();
4016         
4017         Roo.log(target.nodeName.toLowerCase());
4018         
4019         if(target.nodeName.toLowerCase() === 'i'){
4020             e.preventDefault();
4021         }
4022         
4023     },
4024     
4025     onTriggerPress  : function(e)
4026     {
4027         Roo.log('trigger press');
4028         //Roo.log(e.getTarget());
4029        // Roo.log(this.triggerEl.dom);
4030        
4031         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4032         var pel = Roo.get(e.getTarget());
4033         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4034             Roo.log('is treeview or dropdown?');
4035             return;
4036         }
4037         
4038         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4039             return;
4040         }
4041         
4042         if (this.isVisible()) {
4043             Roo.log('hide');
4044             this.hide();
4045         } else {
4046             Roo.log('show');
4047             
4048             this.show(this.triggerEl, this.align, false);
4049         }
4050         
4051         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4052             e.stopEvent();
4053         }
4054         
4055     },
4056        
4057     
4058     hideMenuItems : function()
4059     {
4060         Roo.log("hide Menu Items");
4061         if (!this.el) { 
4062             return;
4063         }
4064         
4065         this.el.select('.open',true).each(function(aa) {
4066             
4067             aa.removeClass('open');
4068          
4069         });
4070     },
4071     addxtypeChild : function (tree, cntr) {
4072         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4073           
4074         this.menuitems.add(comp);
4075         return comp;
4076
4077     },
4078     getEl : function()
4079     {
4080         Roo.log(this.el);
4081         return this.el;
4082     },
4083     
4084     clear : function()
4085     {
4086         this.getEl().dom.innerHTML = '';
4087         this.menuitems.clear();
4088     }
4089 });
4090
4091  
4092  /**
4093  * @class Roo.bootstrap.menu.Item
4094  * @extends Roo.bootstrap.Component
4095  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4096  * @parent Roo.bootstrap.menu.Menu
4097  * @licence LGPL
4098  * Bootstrap MenuItem class
4099  * 
4100  * @cfg {String} html the menu label
4101  * @cfg {String} href the link
4102  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4103  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4104  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4105  * @cfg {String} fa favicon to show on left of menu item.
4106  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4107  * 
4108  * 
4109  * @constructor
4110  * Create a new MenuItem
4111  * @param {Object} config The config object
4112  */
4113
4114
4115 Roo.bootstrap.menu.Item = function(config){
4116     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4117     this.addEvents({
4118         // raw events
4119         /**
4120          * @event click
4121          * The raw click event for the entire grid.
4122          * @param {Roo.bootstrap.menu.Item} this
4123          * @param {Roo.EventObject} e
4124          */
4125         "click" : true
4126     });
4127 };
4128
4129 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4130     
4131     href : false,
4132     html : false,
4133     preventDefault: false,
4134     isContainer : false,
4135     active : false,
4136     fa: false,
4137     
4138     getAutoCreate : function(){
4139         
4140         if(this.isContainer){
4141             return {
4142                 tag: 'li',
4143                 cls: 'dropdown-menu-item '
4144             };
4145         }
4146         var ctag = {
4147             tag: 'span',
4148             html: 'Link'
4149         };
4150         
4151         var anc = {
4152             tag : 'a',
4153             cls : 'dropdown-item',
4154             href : '#',
4155             cn : [  ]
4156         };
4157         
4158         if (this.fa !== false) {
4159             anc.cn.push({
4160                 tag : 'i',
4161                 cls : 'fa fa-' + this.fa
4162             });
4163         }
4164         
4165         anc.cn.push(ctag);
4166         
4167         
4168         var cfg= {
4169             tag: 'li',
4170             cls: 'dropdown-menu-item',
4171             cn: [ anc ]
4172         };
4173         if (this.parent().type == 'treeview') {
4174             cfg.cls = 'treeview-menu';
4175         }
4176         if (this.active) {
4177             cfg.cls += ' active';
4178         }
4179         
4180         
4181         
4182         anc.href = this.href || cfg.cn[0].href ;
4183         ctag.html = this.html || cfg.cn[0].html ;
4184         return cfg;
4185     },
4186     
4187     initEvents: function()
4188     {
4189         if (this.parent().type == 'treeview') {
4190             this.el.select('a').on('click', this.onClick, this);
4191         }
4192         
4193         if (this.menu) {
4194             this.menu.parentType = this.xtype;
4195             this.menu.triggerEl = this.el;
4196             this.menu = this.addxtype(Roo.apply({}, this.menu));
4197         }
4198         
4199     },
4200     onClick : function(e)
4201     {
4202         //Roo.log('item on click ');
4203         
4204         if(this.href === false || this.preventDefault){
4205             e.preventDefault();
4206         }
4207         //this.parent().hideMenuItems();
4208         
4209         this.fireEvent('click', this, e);
4210     },
4211     getEl : function()
4212     {
4213         return this.el;
4214     } 
4215 });
4216
4217  
4218
4219  
4220
4221   
4222 /**
4223  * @class Roo.bootstrap.menu.Separator
4224  * @extends Roo.bootstrap.Component
4225  * @licence LGPL
4226  * @parent Roo.bootstrap.menu.Menu
4227  * Bootstrap Separator class
4228  * 
4229  * @constructor
4230  * Create a new Separator
4231  * @param {Object} config The config object
4232  */
4233
4234
4235 Roo.bootstrap.menu.Separator = function(config){
4236     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4237 };
4238
4239 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4240     
4241     getAutoCreate : function(){
4242         var cfg = {
4243             tag : 'li',
4244             cls: 'dropdown-divider divider'
4245         };
4246         
4247         return cfg;
4248     }
4249    
4250 });
4251
4252  
4253
4254  
4255 /*
4256 * Licence: LGPL
4257 */
4258
4259 /**
4260  * @class Roo.bootstrap.Modal
4261  * @extends Roo.bootstrap.Component
4262  * @parent none builder
4263  * @children Roo.bootstrap.Component
4264  * Bootstrap Modal class
4265  * @cfg {String} title Title of dialog
4266  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4267  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4268  * @cfg {Boolean} specificTitle default false
4269  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4270  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4271  * @cfg {Boolean} animate default true
4272  * @cfg {Boolean} allow_close default true
4273  * @cfg {Boolean} fitwindow default false
4274  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4275  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4276  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4277  * @cfg {String} size (sm|lg|xl) default empty
4278  * @cfg {Number} max_width set the max width of modal
4279  * @cfg {Boolean} editableTitle can the title be edited
4280
4281  *
4282  *
4283  * @constructor
4284  * Create a new Modal Dialog
4285  * @param {Object} config The config object
4286  */
4287
4288 Roo.bootstrap.Modal = function(config){
4289     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4290     this.addEvents({
4291         // raw events
4292         /**
4293          * @event btnclick
4294          * The raw btnclick event for the button
4295          * @param {Roo.EventObject} e
4296          */
4297         "btnclick" : true,
4298         /**
4299          * @event resize
4300          * Fire when dialog resize
4301          * @param {Roo.bootstrap.Modal} this
4302          * @param {Roo.EventObject} e
4303          */
4304         "resize" : true,
4305         /**
4306          * @event titlechanged
4307          * Fire when the editable title has been changed
4308          * @param {Roo.bootstrap.Modal} this
4309          * @param {Roo.EventObject} value
4310          */
4311         "titlechanged" : true 
4312         
4313     });
4314     this.buttons = this.buttons || [];
4315
4316     if (this.tmpl) {
4317         this.tmpl = Roo.factory(this.tmpl);
4318     }
4319
4320 };
4321
4322 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4323
4324     title : 'test dialog',
4325
4326     buttons : false,
4327
4328     // set on load...
4329
4330     html: false,
4331
4332     tmp: false,
4333
4334     specificTitle: false,
4335
4336     buttonPosition: 'right',
4337
4338     allow_close : true,
4339
4340     animate : true,
4341
4342     fitwindow: false,
4343     
4344      // private
4345     dialogEl: false,
4346     bodyEl:  false,
4347     footerEl:  false,
4348     titleEl:  false,
4349     closeEl:  false,
4350
4351     size: '',
4352     
4353     max_width: 0,
4354     
4355     max_height: 0,
4356     
4357     fit_content: false,
4358     editableTitle  : false,
4359
4360     onRender : function(ct, position)
4361     {
4362         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4363
4364         if(!this.el){
4365             var cfg = Roo.apply({},  this.getAutoCreate());
4366             cfg.id = Roo.id();
4367             //if(!cfg.name){
4368             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4369             //}
4370             //if (!cfg.name.length) {
4371             //    delete cfg.name;
4372            // }
4373             if (this.cls) {
4374                 cfg.cls += ' ' + this.cls;
4375             }
4376             if (this.style) {
4377                 cfg.style = this.style;
4378             }
4379             this.el = Roo.get(document.body).createChild(cfg, position);
4380         }
4381         //var type = this.el.dom.type;
4382
4383
4384         if(this.tabIndex !== undefined){
4385             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4386         }
4387
4388         this.dialogEl = this.el.select('.modal-dialog',true).first();
4389         this.bodyEl = this.el.select('.modal-body',true).first();
4390         this.closeEl = this.el.select('.modal-header .close', true).first();
4391         this.headerEl = this.el.select('.modal-header',true).first();
4392         this.titleEl = this.el.select('.modal-title',true).first();
4393         this.footerEl = this.el.select('.modal-footer',true).first();
4394
4395         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4396         
4397         //this.el.addClass("x-dlg-modal");
4398
4399         if (this.buttons.length) {
4400             Roo.each(this.buttons, function(bb) {
4401                 var b = Roo.apply({}, bb);
4402                 b.xns = b.xns || Roo.bootstrap;
4403                 b.xtype = b.xtype || 'Button';
4404                 if (typeof(b.listeners) == 'undefined') {
4405                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4406                 }
4407
4408                 var btn = Roo.factory(b);
4409
4410                 btn.render(this.getButtonContainer());
4411
4412             },this);
4413         }
4414         // render the children.
4415         var nitems = [];
4416
4417         if(typeof(this.items) != 'undefined'){
4418             var items = this.items;
4419             delete this.items;
4420
4421             for(var i =0;i < items.length;i++) {
4422                 // we force children not to montor widnow resize  - as we do that for them.
4423                 items[i].monitorWindowResize = false;
4424                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4425             }
4426         }
4427
4428         this.items = nitems;
4429
4430         // where are these used - they used to be body/close/footer
4431
4432
4433         this.initEvents();
4434         //this.el.addClass([this.fieldClass, this.cls]);
4435
4436     },
4437
4438     getAutoCreate : function()
4439     {
4440         // we will default to modal-body-overflow - might need to remove or make optional later.
4441         var bdy = {
4442                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4443                 html : this.html || ''
4444         };
4445
4446         var title = {
4447             tag: 'h5',
4448             cls : 'modal-title',
4449             html : this.title
4450         };
4451
4452         if(this.specificTitle){ // WTF is this?
4453             title = this.title;
4454         }
4455
4456         var header = [];
4457         if (this.allow_close && Roo.bootstrap.version == 3) {
4458             header.push({
4459                 tag: 'button',
4460                 cls : 'close',
4461                 html : '&times'
4462             });
4463         }
4464
4465         header.push(title);
4466
4467         if (this.editableTitle) {
4468             header.push({
4469                 cls: 'form-control roo-editable-title d-none',
4470                 tag: 'input',
4471                 type: 'text'
4472             });
4473         }
4474         
4475         if (this.allow_close && Roo.bootstrap.version == 4) {
4476             header.push({
4477                 tag: 'button',
4478                 cls : 'close',
4479                 html : '&times'
4480             });
4481         }
4482         
4483         var size = '';
4484
4485         if(this.size.length){
4486             size = 'modal-' + this.size;
4487         }
4488         
4489         var footer = Roo.bootstrap.version == 3 ?
4490             {
4491                 cls : 'modal-footer',
4492                 cn : [
4493                     {
4494                         tag: 'div',
4495                         cls: 'btn-' + this.buttonPosition
4496                     }
4497                 ]
4498
4499             } :
4500             {  // BS4 uses mr-auto on left buttons....
4501                 cls : 'modal-footer'
4502             };
4503
4504             
4505
4506         
4507         
4508         var modal = {
4509             cls: "modal",
4510              cn : [
4511                 {
4512                     cls: "modal-dialog " + size,
4513                     cn : [
4514                         {
4515                             cls : "modal-content",
4516                             cn : [
4517                                 {
4518                                     cls : 'modal-header',
4519                                     cn : header
4520                                 },
4521                                 bdy,
4522                                 footer
4523                             ]
4524
4525                         }
4526                     ]
4527
4528                 }
4529             ]
4530         };
4531
4532         if(this.animate){
4533             modal.cls += ' fade';
4534         }
4535
4536         return modal;
4537
4538     },
4539     getChildContainer : function() {
4540
4541          return this.bodyEl;
4542
4543     },
4544     getButtonContainer : function() {
4545         
4546          return Roo.bootstrap.version == 4 ?
4547             this.el.select('.modal-footer',true).first()
4548             : this.el.select('.modal-footer div',true).first();
4549
4550     },
4551     
4552     closeClick : function()
4553     {
4554         this.hide();
4555     },
4556     
4557     initEvents : function()
4558     {
4559         if (this.allow_close) {
4560             this.closeEl.on('click', this.closeClick, this);
4561         }
4562         Roo.EventManager.onWindowResize(this.resize, this, true);
4563         if (this.editableTitle) {
4564             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4565             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4566             this.headerEditEl.on('keyup', function(e) {
4567                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4568                         this.toggleHeaderInput(false)
4569                     }
4570                 }, this);
4571             this.headerEditEl.on('blur', function(e) {
4572                 this.toggleHeaderInput(false)
4573             },this);
4574         }
4575
4576     },
4577   
4578
4579     resize : function()
4580     {
4581         this.maskEl.setSize(
4582             Roo.lib.Dom.getViewWidth(true),
4583             Roo.lib.Dom.getViewHeight(true)
4584         );
4585         
4586         if (this.fitwindow) {
4587             
4588            this.dialogEl.setStyle( { 'max-width' : '100%' });
4589             this.setSize(
4590                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4591                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4592             );
4593             return;
4594         }
4595         
4596         if(this.max_width !== 0) {
4597             
4598             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4599             
4600             if(this.height) {
4601                 this.setSize(w, this.height);
4602                 return;
4603             }
4604             
4605             if(this.max_height) {
4606                 this.setSize(w,Math.min(
4607                     this.max_height,
4608                     Roo.lib.Dom.getViewportHeight(true) - 60
4609                 ));
4610                 
4611                 return;
4612             }
4613             
4614             if(!this.fit_content) {
4615                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4616                 return;
4617             }
4618             
4619             this.setSize(w, Math.min(
4620                 60 +
4621                 this.headerEl.getHeight() + 
4622                 this.footerEl.getHeight() + 
4623                 this.getChildHeight(this.bodyEl.dom.childNodes),
4624                 Roo.lib.Dom.getViewportHeight(true) - 60)
4625             );
4626         }
4627         
4628     },
4629
4630     setSize : function(w,h)
4631     {
4632         if (!w && !h) {
4633             return;
4634         }
4635         
4636         this.resizeTo(w,h);
4637         // any layout/border etc.. resize..
4638         (function () {
4639             this.items.forEach( function(e) {
4640                 e.layout ? e.layout() : false;
4641
4642             });
4643         }).defer(100,this);
4644         
4645     },
4646
4647     show : function() {
4648
4649         if (!this.rendered) {
4650             this.render();
4651         }
4652         this.toggleHeaderInput(false);
4653         //this.el.setStyle('display', 'block');
4654         this.el.removeClass('hideing');
4655         this.el.dom.style.display='block';
4656         
4657         Roo.get(document.body).addClass('modal-open');
4658  
4659         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4660             
4661             (function(){
4662                 this.el.addClass('show');
4663                 this.el.addClass('in');
4664             }).defer(50, this);
4665         }else{
4666             this.el.addClass('show');
4667             this.el.addClass('in');
4668         }
4669
4670         // not sure how we can show data in here..
4671         //if (this.tmpl) {
4672         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4673         //}
4674
4675         Roo.get(document.body).addClass("x-body-masked");
4676         
4677         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4678         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4679         this.maskEl.dom.style.display = 'block';
4680         this.maskEl.addClass('show');
4681         
4682         
4683         this.resize();
4684         
4685         this.fireEvent('show', this);
4686
4687         // set zindex here - otherwise it appears to be ignored...
4688         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4689         
4690         
4691         // this is for children that are... layout.Border 
4692         (function () {
4693             this.items.forEach( function(e) {
4694                 e.layout ? e.layout() : false;
4695
4696             });
4697         }).defer(100,this);
4698
4699     },
4700     hide : function()
4701     {
4702         if(this.fireEvent("beforehide", this) !== false){
4703             
4704             this.maskEl.removeClass('show');
4705             
4706             this.maskEl.dom.style.display = '';
4707             Roo.get(document.body).removeClass("x-body-masked");
4708             this.el.removeClass('in');
4709             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4710
4711             if(this.animate){ // why
4712                 this.el.addClass('hideing');
4713                 this.el.removeClass('show');
4714                 (function(){
4715                     if (!this.el.hasClass('hideing')) {
4716                         return; // it's been shown again...
4717                     }
4718                     
4719                     this.el.dom.style.display='';
4720
4721                     Roo.get(document.body).removeClass('modal-open');
4722                     this.el.removeClass('hideing');
4723                 }).defer(150,this);
4724                 
4725             }else{
4726                 this.el.removeClass('show');
4727                 this.el.dom.style.display='';
4728                 Roo.get(document.body).removeClass('modal-open');
4729
4730             }
4731             this.fireEvent('hide', this);
4732         }
4733     },
4734     isVisible : function()
4735     {
4736         
4737         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4738         
4739     },
4740
4741     addButton : function(str, cb)
4742     {
4743
4744
4745         var b = Roo.apply({}, { html : str } );
4746         b.xns = b.xns || Roo.bootstrap;
4747         b.xtype = b.xtype || 'Button';
4748         if (typeof(b.listeners) == 'undefined') {
4749             b.listeners = { click : cb.createDelegate(this)  };
4750         }
4751
4752         var btn = Roo.factory(b);
4753
4754         btn.render(this.getButtonContainer());
4755
4756         return btn;
4757
4758     },
4759
4760     setDefaultButton : function(btn)
4761     {
4762         //this.el.select('.modal-footer').()
4763     },
4764
4765     resizeTo: function(w,h)
4766     {
4767         this.dialogEl.setWidth(w);
4768         
4769         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4770
4771         this.bodyEl.setHeight(h - diff);
4772         
4773         this.fireEvent('resize', this);
4774     },
4775     
4776     setContentSize  : function(w, h)
4777     {
4778
4779     },
4780     onButtonClick: function(btn,e)
4781     {
4782         //Roo.log([a,b,c]);
4783         this.fireEvent('btnclick', btn.name, e);
4784     },
4785      /**
4786      * Set the title of the Dialog
4787      * @param {String} str new Title
4788      */
4789     setTitle: function(str) {
4790         this.titleEl.dom.innerHTML = str;
4791         this.title = str;
4792     },
4793     /**
4794      * Set the body of the Dialog
4795      * @param {String} str new Title
4796      */
4797     setBody: function(str) {
4798         this.bodyEl.dom.innerHTML = str;
4799     },
4800     /**
4801      * Set the body of the Dialog using the template
4802      * @param {Obj} data - apply this data to the template and replace the body contents.
4803      */
4804     applyBody: function(obj)
4805     {
4806         if (!this.tmpl) {
4807             Roo.log("Error - using apply Body without a template");
4808             //code
4809         }
4810         this.tmpl.overwrite(this.bodyEl, obj);
4811     },
4812     
4813     getChildHeight : function(child_nodes)
4814     {
4815         if(
4816             !child_nodes ||
4817             child_nodes.length == 0
4818         ) {
4819             return 0;
4820         }
4821         
4822         var child_height = 0;
4823         
4824         for(var i = 0; i < child_nodes.length; i++) {
4825             
4826             /*
4827             * for modal with tabs...
4828             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4829                 
4830                 var layout_childs = child_nodes[i].childNodes;
4831                 
4832                 for(var j = 0; j < layout_childs.length; j++) {
4833                     
4834                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4835                         
4836                         var layout_body_childs = layout_childs[j].childNodes;
4837                         
4838                         for(var k = 0; k < layout_body_childs.length; k++) {
4839                             
4840                             if(layout_body_childs[k].classList.contains('navbar')) {
4841                                 child_height += layout_body_childs[k].offsetHeight;
4842                                 continue;
4843                             }
4844                             
4845                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4846                                 
4847                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4848                                 
4849                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4850                                     
4851                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4852                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4853                                         continue;
4854                                     }
4855                                     
4856                                 }
4857                                 
4858                             }
4859                             
4860                         }
4861                     }
4862                 }
4863                 continue;
4864             }
4865             */
4866             
4867             child_height += child_nodes[i].offsetHeight;
4868             // Roo.log(child_nodes[i].offsetHeight);
4869         }
4870         
4871         return child_height;
4872     },
4873     toggleHeaderInput : function(is_edit)
4874     {
4875         if (!this.editableTitle) {
4876             return; // not editable.
4877         }
4878         if (is_edit && this.is_header_editing) {
4879             return; // already editing..
4880         }
4881         if (is_edit) {
4882     
4883             this.headerEditEl.dom.value = this.title;
4884             this.headerEditEl.removeClass('d-none');
4885             this.headerEditEl.dom.focus();
4886             this.titleEl.addClass('d-none');
4887             
4888             this.is_header_editing = true;
4889             return
4890         }
4891         // flip back to not editing.
4892         this.title = this.headerEditEl.dom.value;
4893         this.headerEditEl.addClass('d-none');
4894         this.titleEl.removeClass('d-none');
4895         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4896         this.is_header_editing = false;
4897         this.fireEvent('titlechanged', this, this.title);
4898     
4899             
4900         
4901     }
4902
4903 });
4904
4905
4906 Roo.apply(Roo.bootstrap.Modal,  {
4907     /**
4908          * Button config that displays a single OK button
4909          * @type Object
4910          */
4911         OK :  [{
4912             name : 'ok',
4913             weight : 'primary',
4914             html : 'OK'
4915         }],
4916         /**
4917          * Button config that displays Yes and No buttons
4918          * @type Object
4919          */
4920         YESNO : [
4921             {
4922                 name  : 'no',
4923                 html : 'No'
4924             },
4925             {
4926                 name  :'yes',
4927                 weight : 'primary',
4928                 html : 'Yes'
4929             }
4930         ],
4931
4932         /**
4933          * Button config that displays OK and Cancel buttons
4934          * @type Object
4935          */
4936         OKCANCEL : [
4937             {
4938                name : 'cancel',
4939                 html : 'Cancel'
4940             },
4941             {
4942                 name : 'ok',
4943                 weight : 'primary',
4944                 html : 'OK'
4945             }
4946         ],
4947         /**
4948          * Button config that displays Yes, No and Cancel buttons
4949          * @type Object
4950          */
4951         YESNOCANCEL : [
4952             {
4953                 name : 'yes',
4954                 weight : 'primary',
4955                 html : 'Yes'
4956             },
4957             {
4958                 name : 'no',
4959                 html : 'No'
4960             },
4961             {
4962                 name : 'cancel',
4963                 html : 'Cancel'
4964             }
4965         ],
4966         
4967         zIndex : 10001
4968 });
4969
4970 /*
4971  * - LGPL
4972  *
4973  * messagebox - can be used as a replace
4974  * 
4975  */
4976 /**
4977  * @class Roo.MessageBox
4978  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4979  * Example usage:
4980  *<pre><code>
4981 // Basic alert:
4982 Roo.Msg.alert('Status', 'Changes saved successfully.');
4983
4984 // Prompt for user data:
4985 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4986     if (btn == 'ok'){
4987         // process text value...
4988     }
4989 });
4990
4991 // Show a dialog using config options:
4992 Roo.Msg.show({
4993    title:'Save Changes?',
4994    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4995    buttons: Roo.Msg.YESNOCANCEL,
4996    fn: processResult,
4997    animEl: 'elId'
4998 });
4999 </code></pre>
5000  * @static
5001  */
5002 Roo.bootstrap.MessageBox = function(){
5003     var dlg, opt, mask, waitTimer;
5004     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5005     var buttons, activeTextEl, bwidth;
5006
5007     
5008     // private
5009     var handleButton = function(button){
5010         dlg.hide();
5011         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5012     };
5013
5014     // private
5015     var handleHide = function(){
5016         if(opt && opt.cls){
5017             dlg.el.removeClass(opt.cls);
5018         }
5019         //if(waitTimer){
5020         //    Roo.TaskMgr.stop(waitTimer);
5021         //    waitTimer = null;
5022         //}
5023     };
5024
5025     // private
5026     var updateButtons = function(b){
5027         var width = 0;
5028         if(!b){
5029             buttons["ok"].hide();
5030             buttons["cancel"].hide();
5031             buttons["yes"].hide();
5032             buttons["no"].hide();
5033             dlg.footerEl.hide();
5034             
5035             return width;
5036         }
5037         dlg.footerEl.show();
5038         for(var k in buttons){
5039             if(typeof buttons[k] != "function"){
5040                 if(b[k]){
5041                     buttons[k].show();
5042                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5043                     width += buttons[k].el.getWidth()+15;
5044                 }else{
5045                     buttons[k].hide();
5046                 }
5047             }
5048         }
5049         return width;
5050     };
5051
5052     // private
5053     var handleEsc = function(d, k, e){
5054         if(opt && opt.closable !== false){
5055             dlg.hide();
5056         }
5057         if(e){
5058             e.stopEvent();
5059         }
5060     };
5061
5062     return {
5063         /**
5064          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5065          * @return {Roo.BasicDialog} The BasicDialog element
5066          */
5067         getDialog : function(){
5068            if(!dlg){
5069                 dlg = new Roo.bootstrap.Modal( {
5070                     //draggable: true,
5071                     //resizable:false,
5072                     //constraintoviewport:false,
5073                     //fixedcenter:true,
5074                     //collapsible : false,
5075                     //shim:true,
5076                     //modal: true,
5077                 //    width: 'auto',
5078                   //  height:100,
5079                     //buttonAlign:"center",
5080                     closeClick : function(){
5081                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5082                             handleButton("no");
5083                         }else{
5084                             handleButton("cancel");
5085                         }
5086                     }
5087                 });
5088                 dlg.render();
5089                 dlg.on("hide", handleHide);
5090                 mask = dlg.mask;
5091                 //dlg.addKeyListener(27, handleEsc);
5092                 buttons = {};
5093                 this.buttons = buttons;
5094                 var bt = this.buttonText;
5095                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5096                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5097                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5098                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5099                 //Roo.log(buttons);
5100                 bodyEl = dlg.bodyEl.createChild({
5101
5102                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5103                         '<textarea class="roo-mb-textarea"></textarea>' +
5104                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5105                 });
5106                 msgEl = bodyEl.dom.firstChild;
5107                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5108                 textboxEl.enableDisplayMode();
5109                 textboxEl.addKeyListener([10,13], function(){
5110                     if(dlg.isVisible() && opt && opt.buttons){
5111                         if(opt.buttons.ok){
5112                             handleButton("ok");
5113                         }else if(opt.buttons.yes){
5114                             handleButton("yes");
5115                         }
5116                     }
5117                 });
5118                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5119                 textareaEl.enableDisplayMode();
5120                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5121                 progressEl.enableDisplayMode();
5122                 
5123                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5124                 var pf = progressEl.dom.firstChild;
5125                 if (pf) {
5126                     pp = Roo.get(pf.firstChild);
5127                     pp.setHeight(pf.offsetHeight);
5128                 }
5129                 
5130             }
5131             return dlg;
5132         },
5133
5134         /**
5135          * Updates the message box body text
5136          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5137          * the XHTML-compliant non-breaking space character '&amp;#160;')
5138          * @return {Roo.MessageBox} This message box
5139          */
5140         updateText : function(text)
5141         {
5142             if(!dlg.isVisible() && !opt.width){
5143                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5144                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5145             }
5146             msgEl.innerHTML = text || '&#160;';
5147       
5148             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5149             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5150             var w = Math.max(
5151                     Math.min(opt.width || cw , this.maxWidth), 
5152                     Math.max(opt.minWidth || this.minWidth, bwidth)
5153             );
5154             if(opt.prompt){
5155                 activeTextEl.setWidth(w);
5156             }
5157             if(dlg.isVisible()){
5158                 dlg.fixedcenter = false;
5159             }
5160             // to big, make it scroll. = But as usual stupid IE does not support
5161             // !important..
5162             
5163             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5164                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5165                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5166             } else {
5167                 bodyEl.dom.style.height = '';
5168                 bodyEl.dom.style.overflowY = '';
5169             }
5170             if (cw > w) {
5171                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5172             } else {
5173                 bodyEl.dom.style.overflowX = '';
5174             }
5175             
5176             dlg.setContentSize(w, bodyEl.getHeight());
5177             if(dlg.isVisible()){
5178                 dlg.fixedcenter = true;
5179             }
5180             return this;
5181         },
5182
5183         /**
5184          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5185          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5186          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5187          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5188          * @return {Roo.MessageBox} This message box
5189          */
5190         updateProgress : function(value, text){
5191             if(text){
5192                 this.updateText(text);
5193             }
5194             
5195             if (pp) { // weird bug on my firefox - for some reason this is not defined
5196                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5197                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5198             }
5199             return this;
5200         },        
5201
5202         /**
5203          * Returns true if the message box is currently displayed
5204          * @return {Boolean} True if the message box is visible, else false
5205          */
5206         isVisible : function(){
5207             return dlg && dlg.isVisible();  
5208         },
5209
5210         /**
5211          * Hides the message box if it is displayed
5212          */
5213         hide : function(){
5214             if(this.isVisible()){
5215                 dlg.hide();
5216             }  
5217         },
5218
5219         /**
5220          * Displays a new message box, or reinitializes an existing message box, based on the config options
5221          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5222          * The following config object properties are supported:
5223          * <pre>
5224 Property    Type             Description
5225 ----------  ---------------  ------------------------------------------------------------------------------------
5226 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5227                                    closes (defaults to undefined)
5228 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5229                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5230 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5231                                    progress and wait dialogs will ignore this property and always hide the
5232                                    close button as they can only be closed programmatically.
5233 cls               String           A custom CSS class to apply to the message box element
5234 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5235                                    displayed (defaults to 75)
5236 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5237                                    function will be btn (the name of the button that was clicked, if applicable,
5238                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5239                                    Progress and wait dialogs will ignore this option since they do not respond to
5240                                    user actions and can only be closed programmatically, so any required function
5241                                    should be called by the same code after it closes the dialog.
5242 icon              String           A CSS class that provides a background image to be used as an icon for
5243                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5244 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5245 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5246 modal             Boolean          False to allow user interaction with the page while the message box is
5247                                    displayed (defaults to true)
5248 msg               String           A string that will replace the existing message box body text (defaults
5249                                    to the XHTML-compliant non-breaking space character '&#160;')
5250 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5251 progress          Boolean          True to display a progress bar (defaults to false)
5252 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5253 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5254 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5255 title             String           The title text
5256 value             String           The string value to set into the active textbox element if displayed
5257 wait              Boolean          True to display a progress bar (defaults to false)
5258 width             Number           The width of the dialog in pixels
5259 </pre>
5260          *
5261          * Example usage:
5262          * <pre><code>
5263 Roo.Msg.show({
5264    title: 'Address',
5265    msg: 'Please enter your address:',
5266    width: 300,
5267    buttons: Roo.MessageBox.OKCANCEL,
5268    multiline: true,
5269    fn: saveAddress,
5270    animEl: 'addAddressBtn'
5271 });
5272 </code></pre>
5273          * @param {Object} config Configuration options
5274          * @return {Roo.MessageBox} This message box
5275          */
5276         show : function(options)
5277         {
5278             
5279             // this causes nightmares if you show one dialog after another
5280             // especially on callbacks..
5281              
5282             if(this.isVisible()){
5283                 
5284                 this.hide();
5285                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5286                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5287                 Roo.log("New Dialog Message:" +  options.msg )
5288                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5289                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5290                 
5291             }
5292             var d = this.getDialog();
5293             opt = options;
5294             d.setTitle(opt.title || "&#160;");
5295             d.closeEl.setDisplayed(opt.closable !== false);
5296             activeTextEl = textboxEl;
5297             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5298             if(opt.prompt){
5299                 if(opt.multiline){
5300                     textboxEl.hide();
5301                     textareaEl.show();
5302                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5303                         opt.multiline : this.defaultTextHeight);
5304                     activeTextEl = textareaEl;
5305                 }else{
5306                     textboxEl.show();
5307                     textareaEl.hide();
5308                 }
5309             }else{
5310                 textboxEl.hide();
5311                 textareaEl.hide();
5312             }
5313             progressEl.setDisplayed(opt.progress === true);
5314             if (opt.progress) {
5315                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5316             }
5317             this.updateProgress(0);
5318             activeTextEl.dom.value = opt.value || "";
5319             if(opt.prompt){
5320                 dlg.setDefaultButton(activeTextEl);
5321             }else{
5322                 var bs = opt.buttons;
5323                 var db = null;
5324                 if(bs && bs.ok){
5325                     db = buttons["ok"];
5326                 }else if(bs && bs.yes){
5327                     db = buttons["yes"];
5328                 }
5329                 dlg.setDefaultButton(db);
5330             }
5331             bwidth = updateButtons(opt.buttons);
5332             this.updateText(opt.msg);
5333             if(opt.cls){
5334                 d.el.addClass(opt.cls);
5335             }
5336             d.proxyDrag = opt.proxyDrag === true;
5337             d.modal = opt.modal !== false;
5338             d.mask = opt.modal !== false ? mask : false;
5339             if(!d.isVisible()){
5340                 // force it to the end of the z-index stack so it gets a cursor in FF
5341                 document.body.appendChild(dlg.el.dom);
5342                 d.animateTarget = null;
5343                 d.show(options.animEl);
5344             }
5345             return this;
5346         },
5347
5348         /**
5349          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5350          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5351          * and closing the message box when the process is complete.
5352          * @param {String} title The title bar text
5353          * @param {String} msg The message box body text
5354          * @return {Roo.MessageBox} This message box
5355          */
5356         progress : function(title, msg){
5357             this.show({
5358                 title : title,
5359                 msg : msg,
5360                 buttons: false,
5361                 progress:true,
5362                 closable:false,
5363                 minWidth: this.minProgressWidth,
5364                 modal : true
5365             });
5366             return this;
5367         },
5368
5369         /**
5370          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5371          * If a callback function is passed it will be called after the user clicks the button, and the
5372          * id of the button that was clicked will be passed as the only parameter to the callback
5373          * (could also be the top-right close button).
5374          * @param {String} title The title bar text
5375          * @param {String} msg The message box body text
5376          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5377          * @param {Object} scope (optional) The scope of the callback function
5378          * @return {Roo.MessageBox} This message box
5379          */
5380         alert : function(title, msg, fn, scope)
5381         {
5382             this.show({
5383                 title : title,
5384                 msg : msg,
5385                 buttons: this.OK,
5386                 fn: fn,
5387                 closable : false,
5388                 scope : scope,
5389                 modal : true
5390             });
5391             return this;
5392         },
5393
5394         /**
5395          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5396          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5397          * You are responsible for closing the message box when the process is complete.
5398          * @param {String} msg The message box body text
5399          * @param {String} title (optional) The title bar text
5400          * @return {Roo.MessageBox} This message box
5401          */
5402         wait : function(msg, title){
5403             this.show({
5404                 title : title,
5405                 msg : msg,
5406                 buttons: false,
5407                 closable:false,
5408                 progress:true,
5409                 modal:true,
5410                 width:300,
5411                 wait:true
5412             });
5413             waitTimer = Roo.TaskMgr.start({
5414                 run: function(i){
5415                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5416                 },
5417                 interval: 1000
5418             });
5419             return this;
5420         },
5421
5422         /**
5423          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5424          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5425          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5426          * @param {String} title The title bar text
5427          * @param {String} msg The message box body text
5428          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5429          * @param {Object} scope (optional) The scope of the callback function
5430          * @return {Roo.MessageBox} This message box
5431          */
5432         confirm : function(title, msg, fn, scope){
5433             this.show({
5434                 title : title,
5435                 msg : msg,
5436                 buttons: this.YESNO,
5437                 fn: fn,
5438                 scope : scope,
5439                 modal : true
5440             });
5441             return this;
5442         },
5443
5444         /**
5445          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5446          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5447          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5448          * (could also be the top-right close button) and the text that was entered will be passed as the two
5449          * parameters to the callback.
5450          * @param {String} title The title bar text
5451          * @param {String} msg The message box body text
5452          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5453          * @param {Object} scope (optional) The scope of the callback function
5454          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5455          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5456          * @return {Roo.MessageBox} This message box
5457          */
5458         prompt : function(title, msg, fn, scope, multiline){
5459             this.show({
5460                 title : title,
5461                 msg : msg,
5462                 buttons: this.OKCANCEL,
5463                 fn: fn,
5464                 minWidth:250,
5465                 scope : scope,
5466                 prompt:true,
5467                 multiline: multiline,
5468                 modal : true
5469             });
5470             return this;
5471         },
5472
5473         /**
5474          * Button config that displays a single OK button
5475          * @type Object
5476          */
5477         OK : {ok:true},
5478         /**
5479          * Button config that displays Yes and No buttons
5480          * @type Object
5481          */
5482         YESNO : {yes:true, no:true},
5483         /**
5484          * Button config that displays OK and Cancel buttons
5485          * @type Object
5486          */
5487         OKCANCEL : {ok:true, cancel:true},
5488         /**
5489          * Button config that displays Yes, No and Cancel buttons
5490          * @type Object
5491          */
5492         YESNOCANCEL : {yes:true, no:true, cancel:true},
5493
5494         /**
5495          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5496          * @type Number
5497          */
5498         defaultTextHeight : 75,
5499         /**
5500          * The maximum width in pixels of the message box (defaults to 600)
5501          * @type Number
5502          */
5503         maxWidth : 600,
5504         /**
5505          * The minimum width in pixels of the message box (defaults to 100)
5506          * @type Number
5507          */
5508         minWidth : 100,
5509         /**
5510          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5511          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5512          * @type Number
5513          */
5514         minProgressWidth : 250,
5515         /**
5516          * An object containing the default button text strings that can be overriden for localized language support.
5517          * Supported properties are: ok, cancel, yes and no.
5518          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5519          * @type Object
5520          */
5521         buttonText : {
5522             ok : "OK",
5523             cancel : "Cancel",
5524             yes : "Yes",
5525             no : "No"
5526         }
5527     };
5528 }();
5529
5530 /**
5531  * Shorthand for {@link Roo.MessageBox}
5532  */
5533 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5534 Roo.Msg = Roo.Msg || Roo.MessageBox;
5535 /*
5536  * - LGPL
5537  *
5538  * navbar
5539  * 
5540  */
5541
5542 /**
5543  * @class Roo.bootstrap.nav.Bar
5544  * @extends Roo.bootstrap.Component
5545  * @abstract
5546  * Bootstrap Navbar class
5547
5548  * @constructor
5549  * Create a new Navbar
5550  * @param {Object} config The config object
5551  */
5552
5553
5554 Roo.bootstrap.nav.Bar = function(config){
5555     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5556     this.addEvents({
5557         // raw events
5558         /**
5559          * @event beforetoggle
5560          * Fire before toggle the menu
5561          * @param {Roo.EventObject} e
5562          */
5563         "beforetoggle" : true
5564     });
5565 };
5566
5567 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5568     
5569     
5570    
5571     // private
5572     navItems : false,
5573     loadMask : false,
5574     
5575     
5576     getAutoCreate : function(){
5577         
5578         
5579         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5580         
5581     },
5582     
5583     initEvents :function ()
5584     {
5585         //Roo.log(this.el.select('.navbar-toggle',true));
5586         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5587         
5588         var mark = {
5589             tag: "div",
5590             cls:"x-dlg-mask"
5591         };
5592         
5593         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5594         
5595         var size = this.el.getSize();
5596         this.maskEl.setSize(size.width, size.height);
5597         this.maskEl.enableDisplayMode("block");
5598         this.maskEl.hide();
5599         
5600         if(this.loadMask){
5601             this.maskEl.show();
5602         }
5603     },
5604     
5605     
5606     getChildContainer : function()
5607     {
5608         if (this.el && this.el.select('.collapse').getCount()) {
5609             return this.el.select('.collapse',true).first();
5610         }
5611         
5612         return this.el;
5613     },
5614     
5615     mask : function()
5616     {
5617         this.maskEl.show();
5618     },
5619     
5620     unmask : function()
5621     {
5622         this.maskEl.hide();
5623     },
5624     onToggle : function()
5625     {
5626         
5627         if(this.fireEvent('beforetoggle', this) === false){
5628             return;
5629         }
5630         var ce = this.el.select('.navbar-collapse',true).first();
5631       
5632         if (!ce.hasClass('show')) {
5633            this.expand();
5634         } else {
5635             this.collapse();
5636         }
5637         
5638         
5639     
5640     },
5641     /**
5642      * Expand the navbar pulldown 
5643      */
5644     expand : function ()
5645     {
5646        
5647         var ce = this.el.select('.navbar-collapse',true).first();
5648         if (ce.hasClass('collapsing')) {
5649             return;
5650         }
5651         ce.dom.style.height = '';
5652                // show it...
5653         ce.addClass('in'); // old...
5654         ce.removeClass('collapse');
5655         ce.addClass('show');
5656         var h = ce.getHeight();
5657         Roo.log(h);
5658         ce.removeClass('show');
5659         // at this point we should be able to see it..
5660         ce.addClass('collapsing');
5661         
5662         ce.setHeight(0); // resize it ...
5663         ce.on('transitionend', function() {
5664             //Roo.log('done transition');
5665             ce.removeClass('collapsing');
5666             ce.addClass('show');
5667             ce.removeClass('collapse');
5668
5669             ce.dom.style.height = '';
5670         }, this, { single: true} );
5671         ce.setHeight(h);
5672         ce.dom.scrollTop = 0;
5673     },
5674     /**
5675      * Collapse the navbar pulldown 
5676      */
5677     collapse : function()
5678     {
5679          var ce = this.el.select('.navbar-collapse',true).first();
5680        
5681         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5682             // it's collapsed or collapsing..
5683             return;
5684         }
5685         ce.removeClass('in'); // old...
5686         ce.setHeight(ce.getHeight());
5687         ce.removeClass('show');
5688         ce.addClass('collapsing');
5689         
5690         ce.on('transitionend', function() {
5691             ce.dom.style.height = '';
5692             ce.removeClass('collapsing');
5693             ce.addClass('collapse');
5694         }, this, { single: true} );
5695         ce.setHeight(0);
5696     }
5697     
5698     
5699     
5700 });
5701
5702
5703
5704  
5705
5706  /*
5707  * - LGPL
5708  *
5709  * navbar
5710  * 
5711  */
5712
5713 /**
5714  * @class Roo.bootstrap.nav.Simplebar
5715  * @extends Roo.bootstrap.nav.Bar
5716  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5717  * Bootstrap Sidebar class
5718  *
5719  * @cfg {Boolean} inverse is inverted color
5720  * 
5721  * @cfg {String} type (nav | pills | tabs)
5722  * @cfg {Boolean} arrangement stacked | justified
5723  * @cfg {String} align (left | right) alignment
5724  * 
5725  * @cfg {Boolean} main (true|false) main nav bar? default false
5726  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5727  * 
5728  * @cfg {String} tag (header|footer|nav|div) default is nav 
5729
5730  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5731  * 
5732  * 
5733  * @constructor
5734  * Create a new Sidebar
5735  * @param {Object} config The config object
5736  */
5737
5738
5739 Roo.bootstrap.nav.Simplebar = function(config){
5740     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5741 };
5742
5743 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5744     
5745     inverse: false,
5746     
5747     type: false,
5748     arrangement: '',
5749     align : false,
5750     
5751     weight : 'light',
5752     
5753     main : false,
5754     
5755     
5756     tag : false,
5757     
5758     
5759     getAutoCreate : function(){
5760         
5761         
5762         var cfg = {
5763             tag : this.tag || 'div',
5764             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5765         };
5766         if (['light','white'].indexOf(this.weight) > -1) {
5767             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5768         }
5769         cfg.cls += ' bg-' + this.weight;
5770         
5771         if (this.inverse) {
5772             cfg.cls += ' navbar-inverse';
5773             
5774         }
5775         
5776         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5777         
5778         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5779             return cfg;
5780         }
5781         
5782         
5783     
5784         
5785         cfg.cn = [
5786             {
5787                 cls: 'nav nav-' + this.xtype,
5788                 tag : 'ul'
5789             }
5790         ];
5791         
5792          
5793         this.type = this.type || 'nav';
5794         if (['tabs','pills'].indexOf(this.type) != -1) {
5795             cfg.cn[0].cls += ' nav-' + this.type
5796         
5797         
5798         } else {
5799             if (this.type!=='nav') {
5800                 Roo.log('nav type must be nav/tabs/pills')
5801             }
5802             cfg.cn[0].cls += ' navbar-nav'
5803         }
5804         
5805         
5806         
5807         
5808         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5809             cfg.cn[0].cls += ' nav-' + this.arrangement;
5810         }
5811         
5812         
5813         if (this.align === 'right') {
5814             cfg.cn[0].cls += ' navbar-right';
5815         }
5816         
5817         
5818         
5819         
5820         return cfg;
5821     
5822         
5823     }
5824     
5825     
5826     
5827 });
5828
5829
5830
5831  
5832
5833  
5834        /*
5835  * - LGPL
5836  *
5837  * navbar
5838  * navbar-fixed-top
5839  * navbar-expand-md  fixed-top 
5840  */
5841
5842 /**
5843  * @class Roo.bootstrap.nav.Headerbar
5844  * @extends Roo.bootstrap.nav.Simplebar
5845  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5846  * Bootstrap Sidebar class
5847  *
5848  * @cfg {String} brand what is brand
5849  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5850  * @cfg {String} brand_href href of the brand
5851  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5852  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5853  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5854  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5855  * 
5856  * @constructor
5857  * Create a new Sidebar
5858  * @param {Object} config The config object
5859  */
5860
5861
5862 Roo.bootstrap.nav.Headerbar = function(config){
5863     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5864       
5865 };
5866
5867 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5868     
5869     position: '',
5870     brand: '',
5871     brand_href: false,
5872     srButton : true,
5873     autohide : false,
5874     desktopCenter : false,
5875    
5876     
5877     getAutoCreate : function(){
5878         
5879         var   cfg = {
5880             tag: this.nav || 'nav',
5881             cls: 'navbar navbar-expand-md',
5882             role: 'navigation',
5883             cn: []
5884         };
5885         
5886         var cn = cfg.cn;
5887         if (this.desktopCenter) {
5888             cn.push({cls : 'container', cn : []});
5889             cn = cn[0].cn;
5890         }
5891         
5892         if(this.srButton){
5893             var btn = {
5894                 tag: 'button',
5895                 type: 'button',
5896                 cls: 'navbar-toggle navbar-toggler',
5897                 'data-toggle': 'collapse',
5898                 cn: [
5899                     {
5900                         tag: 'span',
5901                         cls: 'sr-only',
5902                         html: 'Toggle navigation'
5903                     },
5904                     {
5905                         tag: 'span',
5906                         cls: 'icon-bar navbar-toggler-icon'
5907                     },
5908                     {
5909                         tag: 'span',
5910                         cls: 'icon-bar'
5911                     },
5912                     {
5913                         tag: 'span',
5914                         cls: 'icon-bar'
5915                     }
5916                 ]
5917             };
5918             
5919             cn.push( Roo.bootstrap.version == 4 ? btn : {
5920                 tag: 'div',
5921                 cls: 'navbar-header',
5922                 cn: [
5923                     btn
5924                 ]
5925             });
5926         }
5927         
5928         cn.push({
5929             tag: 'div',
5930             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5931             cn : []
5932         });
5933         
5934         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5935         
5936         if (['light','white'].indexOf(this.weight) > -1) {
5937             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5938         }
5939         cfg.cls += ' bg-' + this.weight;
5940         
5941         
5942         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5943             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5944             
5945             // tag can override this..
5946             
5947             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5948         }
5949         
5950         if (this.brand !== '') {
5951             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5952             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5953                 tag: 'a',
5954                 href: this.brand_href ? this.brand_href : '#',
5955                 cls: 'navbar-brand',
5956                 cn: [
5957                 this.brand
5958                 ]
5959             });
5960         }
5961         
5962         if(this.main){
5963             cfg.cls += ' main-nav';
5964         }
5965         
5966         
5967         return cfg;
5968
5969         
5970     },
5971     getHeaderChildContainer : function()
5972     {
5973         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5974             return this.el.select('.navbar-header',true).first();
5975         }
5976         
5977         return this.getChildContainer();
5978     },
5979     
5980     getChildContainer : function()
5981     {
5982          
5983         return this.el.select('.roo-navbar-collapse',true).first();
5984          
5985         
5986     },
5987     
5988     initEvents : function()
5989     {
5990         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5991         
5992         if (this.autohide) {
5993             
5994             var prevScroll = 0;
5995             var ft = this.el;
5996             
5997             Roo.get(document).on('scroll',function(e) {
5998                 var ns = Roo.get(document).getScroll().top;
5999                 var os = prevScroll;
6000                 prevScroll = ns;
6001                 
6002                 if(ns > os){
6003                     ft.removeClass('slideDown');
6004                     ft.addClass('slideUp');
6005                     return;
6006                 }
6007                 ft.removeClass('slideUp');
6008                 ft.addClass('slideDown');
6009                  
6010               
6011           },this);
6012         }
6013     }    
6014     
6015 });
6016
6017
6018
6019  
6020
6021  /*
6022  * - LGPL
6023  *
6024  * navbar
6025  * 
6026  */
6027
6028 /**
6029  * @class Roo.bootstrap.nav.Sidebar
6030  * @extends Roo.bootstrap.nav.Bar
6031  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6032  * Bootstrap Sidebar class
6033  * 
6034  * @constructor
6035  * Create a new Sidebar
6036  * @param {Object} config The config object
6037  */
6038
6039
6040 Roo.bootstrap.nav.Sidebar = function(config){
6041     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6042 };
6043
6044 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6045     
6046     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6047     
6048     getAutoCreate : function(){
6049         
6050         
6051         return  {
6052             tag: 'div',
6053             cls: 'sidebar sidebar-nav'
6054         };
6055     
6056         
6057     }
6058     
6059     
6060     
6061 });
6062
6063
6064
6065  
6066
6067  /*
6068  * - LGPL
6069  *
6070  * nav group
6071  * 
6072  */
6073
6074 /**
6075  * @class Roo.bootstrap.nav.Group
6076  * @extends Roo.bootstrap.Component
6077  * @children Roo.bootstrap.nav.Item
6078  * Bootstrap NavGroup class
6079  * @cfg {String} align (left|right)
6080  * @cfg {Boolean} inverse
6081  * @cfg {String} type (nav|pills|tab) default nav
6082  * @cfg {String} navId - reference Id for navbar.
6083  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6084  * 
6085  * @constructor
6086  * Create a new nav group
6087  * @param {Object} config The config object
6088  */
6089
6090 Roo.bootstrap.nav.Group = function(config){
6091     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6092     this.navItems = [];
6093    
6094     Roo.bootstrap.nav.Group.register(this);
6095      this.addEvents({
6096         /**
6097              * @event changed
6098              * Fires when the active item changes
6099              * @param {Roo.bootstrap.nav.Group} this
6100              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6101              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6102          */
6103         'changed': true
6104      });
6105     
6106 };
6107
6108 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6109     
6110     align: '',
6111     inverse: false,
6112     form: false,
6113     type: 'nav',
6114     navId : '',
6115     // private
6116     pilltype : true,
6117     
6118     navItems : false, 
6119     
6120     getAutoCreate : function()
6121     {
6122         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6123         
6124         cfg = {
6125             tag : 'ul',
6126             cls: 'nav' 
6127         };
6128         if (Roo.bootstrap.version == 4) {
6129             if (['tabs','pills'].indexOf(this.type) != -1) {
6130                 cfg.cls += ' nav-' + this.type; 
6131             } else {
6132                 // trying to remove so header bar can right align top?
6133                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6134                     // do not use on header bar... 
6135                     cfg.cls += ' navbar-nav';
6136                 }
6137             }
6138             
6139         } else {
6140             if (['tabs','pills'].indexOf(this.type) != -1) {
6141                 cfg.cls += ' nav-' + this.type
6142             } else {
6143                 if (this.type !== 'nav') {
6144                     Roo.log('nav type must be nav/tabs/pills')
6145                 }
6146                 cfg.cls += ' navbar-nav'
6147             }
6148         }
6149         
6150         if (this.parent() && this.parent().sidebar) {
6151             cfg = {
6152                 tag: 'ul',
6153                 cls: 'dashboard-menu sidebar-menu'
6154             };
6155             
6156             return cfg;
6157         }
6158         
6159         if (this.form === true) {
6160             cfg = {
6161                 tag: 'form',
6162                 cls: 'navbar-form form-inline'
6163             };
6164             //nav navbar-right ml-md-auto
6165             if (this.align === 'right') {
6166                 cfg.cls += ' navbar-right ml-md-auto';
6167             } else {
6168                 cfg.cls += ' navbar-left';
6169             }
6170         }
6171         
6172         if (this.align === 'right') {
6173             cfg.cls += ' navbar-right ml-md-auto';
6174         } else {
6175             cfg.cls += ' mr-auto';
6176         }
6177         
6178         if (this.inverse) {
6179             cfg.cls += ' navbar-inverse';
6180             
6181         }
6182         
6183         
6184         return cfg;
6185     },
6186     /**
6187     * sets the active Navigation item
6188     * @param {Roo.bootstrap.nav.Item} the new current navitem
6189     */
6190     setActiveItem : function(item)
6191     {
6192         var prev = false;
6193         Roo.each(this.navItems, function(v){
6194             if (v == item) {
6195                 return ;
6196             }
6197             if (v.isActive()) {
6198                 v.setActive(false, true);
6199                 prev = v;
6200                 
6201             }
6202             
6203         });
6204
6205         item.setActive(true, true);
6206         this.fireEvent('changed', this, item, prev);
6207         
6208         
6209     },
6210     /**
6211     * gets the active Navigation item
6212     * @return {Roo.bootstrap.nav.Item} the current navitem
6213     */
6214     getActive : function()
6215     {
6216         
6217         var prev = false;
6218         Roo.each(this.navItems, function(v){
6219             
6220             if (v.isActive()) {
6221                 prev = v;
6222                 
6223             }
6224             
6225         });
6226         return prev;
6227     },
6228     
6229     indexOfNav : function()
6230     {
6231         
6232         var prev = false;
6233         Roo.each(this.navItems, function(v,i){
6234             
6235             if (v.isActive()) {
6236                 prev = i;
6237                 
6238             }
6239             
6240         });
6241         return prev;
6242     },
6243     /**
6244     * adds a Navigation item
6245     * @param {Roo.bootstrap.nav.Item} the navitem to add
6246     */
6247     addItem : function(cfg)
6248     {
6249         if (this.form && Roo.bootstrap.version == 4) {
6250             cfg.tag = 'div';
6251         }
6252         var cn = new Roo.bootstrap.nav.Item(cfg);
6253         this.register(cn);
6254         cn.parentId = this.id;
6255         cn.onRender(this.el, null);
6256         return cn;
6257     },
6258     /**
6259     * register a Navigation item
6260     * @param {Roo.bootstrap.nav.Item} the navitem to add
6261     */
6262     register : function(item)
6263     {
6264         this.navItems.push( item);
6265         item.navId = this.navId;
6266     
6267     },
6268     
6269     /**
6270     * clear all the Navigation item
6271     */
6272    
6273     clearAll : function()
6274     {
6275         this.navItems = [];
6276         this.el.dom.innerHTML = '';
6277     },
6278     
6279     getNavItem: function(tabId)
6280     {
6281         var ret = false;
6282         Roo.each(this.navItems, function(e) {
6283             if (e.tabId == tabId) {
6284                ret =  e;
6285                return false;
6286             }
6287             return true;
6288             
6289         });
6290         return ret;
6291     },
6292     
6293     setActiveNext : function()
6294     {
6295         var i = this.indexOfNav(this.getActive());
6296         if (i > this.navItems.length) {
6297             return;
6298         }
6299         this.setActiveItem(this.navItems[i+1]);
6300     },
6301     setActivePrev : function()
6302     {
6303         var i = this.indexOfNav(this.getActive());
6304         if (i  < 1) {
6305             return;
6306         }
6307         this.setActiveItem(this.navItems[i-1]);
6308     },
6309     clearWasActive : function(except) {
6310         Roo.each(this.navItems, function(e) {
6311             if (e.tabId != except.tabId && e.was_active) {
6312                e.was_active = false;
6313                return false;
6314             }
6315             return true;
6316             
6317         });
6318     },
6319     getWasActive : function ()
6320     {
6321         var r = false;
6322         Roo.each(this.navItems, function(e) {
6323             if (e.was_active) {
6324                r = e;
6325                return false;
6326             }
6327             return true;
6328             
6329         });
6330         return r;
6331     }
6332     
6333     
6334 });
6335
6336  
6337 Roo.apply(Roo.bootstrap.nav.Group, {
6338     
6339     groups: {},
6340      /**
6341     * register a Navigation Group
6342     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6343     */
6344     register : function(navgrp)
6345     {
6346         this.groups[navgrp.navId] = navgrp;
6347         
6348     },
6349     /**
6350     * fetch a Navigation Group based on the navigation ID
6351     * @param {string} the navgroup to add
6352     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6353     */
6354     get: function(navId) {
6355         if (typeof(this.groups[navId]) == 'undefined') {
6356             return false;
6357             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6358         }
6359         return this.groups[navId] ;
6360     }
6361     
6362     
6363     
6364 });
6365
6366  /**
6367  * @class Roo.bootstrap.nav.Item
6368  * @extends Roo.bootstrap.Component
6369  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6370  * @parent Roo.bootstrap.nav.Group
6371  * @licence LGPL
6372  * Bootstrap Navbar.NavItem class
6373  * 
6374  * @cfg {String} href  link to
6375  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6376  * @cfg {Boolean} button_outline show and outlined button
6377  * @cfg {String} html content of button
6378  * @cfg {String} badge text inside badge
6379  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6380  * @cfg {String} glyphicon DEPRICATED - use fa
6381  * @cfg {String} icon DEPRICATED - use fa
6382  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6383  * @cfg {Boolean} active Is item active
6384  * @cfg {Boolean} disabled Is item disabled
6385  * @cfg {String} linkcls  Link Class
6386  * @cfg {Boolean} preventDefault (true | false) default false
6387  * @cfg {String} tabId the tab that this item activates.
6388  * @cfg {String} tagtype (a|span) render as a href or span?
6389  * @cfg {Boolean} animateRef (true|false) link to element default false  
6390  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6391   
6392  * @constructor
6393  * Create a new Navbar Item
6394  * @param {Object} config The config object
6395  */
6396 Roo.bootstrap.nav.Item = function(config){
6397     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6398     this.addEvents({
6399         // raw events
6400         /**
6401          * @event click
6402          * The raw click event for the entire grid.
6403          * @param {Roo.EventObject} e
6404          */
6405         "click" : true,
6406          /**
6407             * @event changed
6408             * Fires when the active item active state changes
6409             * @param {Roo.bootstrap.nav.Item} this
6410             * @param {boolean} state the new state
6411              
6412          */
6413         'changed': true,
6414         /**
6415             * @event scrollto
6416             * Fires when scroll to element
6417             * @param {Roo.bootstrap.nav.Item} this
6418             * @param {Object} options
6419             * @param {Roo.EventObject} e
6420              
6421          */
6422         'scrollto': true
6423     });
6424    
6425 };
6426
6427 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6428     
6429     href: false,
6430     html: '',
6431     badge: '',
6432     icon: false,
6433     fa : false,
6434     glyphicon: false,
6435     active: false,
6436     preventDefault : false,
6437     tabId : false,
6438     tagtype : 'a',
6439     tag: 'li',
6440     disabled : false,
6441     animateRef : false,
6442     was_active : false,
6443     button_weight : '',
6444     button_outline : false,
6445     linkcls : '',
6446     navLink: false,
6447     
6448     getAutoCreate : function(){
6449          
6450         var cfg = {
6451             tag: this.tag,
6452             cls: 'nav-item'
6453         };
6454         
6455         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6456         
6457         if (this.active) {
6458             cfg.cls +=  ' active' ;
6459         }
6460         if (this.disabled) {
6461             cfg.cls += ' disabled';
6462         }
6463         
6464         // BS4 only?
6465         if (this.button_weight.length) {
6466             cfg.tag = this.href ? 'a' : 'button';
6467             cfg.html = this.html || '';
6468             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6469             if (this.href) {
6470                 cfg.href = this.href;
6471             }
6472             if (this.fa) {
6473                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6474             } else {
6475                 cfg.cls += " nav-html";
6476             }
6477             
6478             // menu .. should add dropdown-menu class - so no need for carat..
6479             
6480             if (this.badge !== '') {
6481                  
6482                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6483             }
6484             return cfg;
6485         }
6486         
6487         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6488             cfg.cn = [
6489                 {
6490                     tag: this.tagtype,
6491                     href : this.href || "#",
6492                     html: this.html || '',
6493                     cls : ''
6494                 }
6495             ];
6496             if (this.tagtype == 'a') {
6497                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6498         
6499             }
6500             if (this.icon) {
6501                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6502             } else  if (this.fa) {
6503                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6504             } else if(this.glyphicon) {
6505                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6506             } else {
6507                 cfg.cn[0].cls += " nav-html";
6508             }
6509             
6510             if (this.menu) {
6511                 cfg.cn[0].html += " <span class='caret'></span>";
6512              
6513             }
6514             
6515             if (this.badge !== '') {
6516                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6517             }
6518         }
6519         
6520         
6521         
6522         return cfg;
6523     },
6524     onRender : function(ct, position)
6525     {
6526        // Roo.log("Call onRender: " + this.xtype);
6527         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6528             this.tag = 'div';
6529         }
6530         
6531         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6532         this.navLink = this.el.select('.nav-link',true).first();
6533         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6534         return ret;
6535     },
6536       
6537     
6538     initEvents: function() 
6539     {
6540         if (typeof (this.menu) != 'undefined') {
6541             this.menu.parentType = this.xtype;
6542             this.menu.triggerEl = this.el;
6543             this.menu = this.addxtype(Roo.apply({}, this.menu));
6544         }
6545         
6546         this.el.on('click', this.onClick, this);
6547         
6548         //if(this.tagtype == 'span'){
6549         //    this.el.select('span',true).on('click', this.onClick, this);
6550         //}
6551        
6552         // at this point parent should be available..
6553         this.parent().register(this);
6554     },
6555     
6556     onClick : function(e)
6557     {
6558         if (e.getTarget('.dropdown-menu-item')) {
6559             // did you click on a menu itemm.... - then don't trigger onclick..
6560             return;
6561         }
6562         
6563         if(
6564                 this.preventDefault ||
6565                                 this.href === false ||
6566                 this.href === '#' 
6567         ){
6568             //Roo.log("NavItem - prevent Default?");
6569             e.preventDefault();
6570         }
6571         
6572         if (this.disabled) {
6573             return;
6574         }
6575         
6576         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6577         if (tg && tg.transition) {
6578             Roo.log("waiting for the transitionend");
6579             return;
6580         }
6581         
6582         
6583         
6584         //Roo.log("fire event clicked");
6585         if(this.fireEvent('click', this, e) === false){
6586             return;
6587         };
6588         
6589         if(this.tagtype == 'span'){
6590             return;
6591         }
6592         
6593         //Roo.log(this.href);
6594         var ael = this.el.select('a',true).first();
6595         //Roo.log(ael);
6596         
6597         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6598             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6599             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6600                 return; // ignore... - it's a 'hash' to another page.
6601             }
6602             Roo.log("NavItem - prevent Default?");
6603             e.preventDefault();
6604             this.scrollToElement(e);
6605         }
6606         
6607         
6608         var p =  this.parent();
6609    
6610         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6611             if (typeof(p.setActiveItem) !== 'undefined') {
6612                 p.setActiveItem(this);
6613             }
6614         }
6615         
6616         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6617         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6618             // remove the collapsed menu expand...
6619             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6620         }
6621     },
6622     
6623     isActive: function () {
6624         return this.active
6625     },
6626     setActive : function(state, fire, is_was_active)
6627     {
6628         if (this.active && !state && this.navId) {
6629             this.was_active = true;
6630             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6631             if (nv) {
6632                 nv.clearWasActive(this);
6633             }
6634             
6635         }
6636         this.active = state;
6637         
6638         if (!state ) {
6639             this.el.removeClass('active');
6640             this.navLink ? this.navLink.removeClass('active') : false;
6641         } else if (!this.el.hasClass('active')) {
6642             
6643             this.el.addClass('active');
6644             if (Roo.bootstrap.version == 4 && this.navLink ) {
6645                 this.navLink.addClass('active');
6646             }
6647             
6648         }
6649         if (fire) {
6650             this.fireEvent('changed', this, state);
6651         }
6652         
6653         // show a panel if it's registered and related..
6654         
6655         if (!this.navId || !this.tabId || !state || is_was_active) {
6656             return;
6657         }
6658         
6659         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6660         if (!tg) {
6661             return;
6662         }
6663         var pan = tg.getPanelByName(this.tabId);
6664         if (!pan) {
6665             return;
6666         }
6667         // if we can not flip to new panel - go back to old nav highlight..
6668         if (false == tg.showPanel(pan)) {
6669             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6670             if (nv) {
6671                 var onav = nv.getWasActive();
6672                 if (onav) {
6673                     onav.setActive(true, false, true);
6674                 }
6675             }
6676             
6677         }
6678         
6679         
6680         
6681     },
6682      // this should not be here...
6683     setDisabled : function(state)
6684     {
6685         this.disabled = state;
6686         if (!state ) {
6687             this.el.removeClass('disabled');
6688         } else if (!this.el.hasClass('disabled')) {
6689             this.el.addClass('disabled');
6690         }
6691         
6692     },
6693     
6694     /**
6695      * Fetch the element to display the tooltip on.
6696      * @return {Roo.Element} defaults to this.el
6697      */
6698     tooltipEl : function()
6699     {
6700         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6701     },
6702     
6703     scrollToElement : function(e)
6704     {
6705         var c = document.body;
6706         
6707         /*
6708          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6709          */
6710         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6711             c = document.documentElement;
6712         }
6713         
6714         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6715         
6716         if(!target){
6717             return;
6718         }
6719
6720         var o = target.calcOffsetsTo(c);
6721         
6722         var options = {
6723             target : target,
6724             value : o[1]
6725         };
6726         
6727         this.fireEvent('scrollto', this, options, e);
6728         
6729         Roo.get(c).scrollTo('top', options.value, true);
6730         
6731         return;
6732     },
6733     /**
6734      * Set the HTML (text content) of the item
6735      * @param {string} html  content for the nav item
6736      */
6737     setHtml : function(html)
6738     {
6739         this.html = html;
6740         this.htmlEl.dom.innerHTML = html;
6741         
6742     } 
6743 });
6744  
6745
6746  /*
6747  * - LGPL
6748  *
6749  * sidebar item
6750  *
6751  *  li
6752  *    <span> icon </span>
6753  *    <span> text </span>
6754  *    <span>badge </span>
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.nav.SidebarItem
6759  * @extends Roo.bootstrap.nav.Item
6760  * Bootstrap Navbar.NavSidebarItem class
6761  * 
6762  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6763  * {Boolean} open is the menu open
6764  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6765  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6766  * {String} buttonSize (sm|md|lg)the extra classes for the button
6767  * {Boolean} showArrow show arrow next to the text (default true)
6768  * @constructor
6769  * Create a new Navbar Button
6770  * @param {Object} config The config object
6771  */
6772 Roo.bootstrap.nav.SidebarItem = function(config){
6773     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6774     this.addEvents({
6775         // raw events
6776         /**
6777          * @event click
6778          * The raw click event for the entire grid.
6779          * @param {Roo.EventObject} e
6780          */
6781         "click" : true,
6782          /**
6783             * @event changed
6784             * Fires when the active item active state changes
6785             * @param {Roo.bootstrap.nav.SidebarItem} this
6786             * @param {boolean} state the new state
6787              
6788          */
6789         'changed': true
6790     });
6791    
6792 };
6793
6794 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6795     
6796     badgeWeight : 'default',
6797     
6798     open: false,
6799     
6800     buttonView : false,
6801     
6802     buttonWeight : 'default',
6803     
6804     buttonSize : 'md',
6805     
6806     showArrow : true,
6807     
6808     getAutoCreate : function(){
6809         
6810         
6811         var a = {
6812                 tag: 'a',
6813                 href : this.href || '#',
6814                 cls: '',
6815                 html : '',
6816                 cn : []
6817         };
6818         
6819         if(this.buttonView){
6820             a = {
6821                 tag: 'button',
6822                 href : this.href || '#',
6823                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6824                 html : this.html,
6825                 cn : []
6826             };
6827         }
6828         
6829         var cfg = {
6830             tag: 'li',
6831             cls: '',
6832             cn: [ a ]
6833         };
6834         
6835         if (this.active) {
6836             cfg.cls += ' active';
6837         }
6838         
6839         if (this.disabled) {
6840             cfg.cls += ' disabled';
6841         }
6842         if (this.open) {
6843             cfg.cls += ' open x-open';
6844         }
6845         // left icon..
6846         if (this.glyphicon || this.icon) {
6847             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6848             a.cn.push({ tag : 'i', cls : c }) ;
6849         }
6850         
6851         if(!this.buttonView){
6852             var span = {
6853                 tag: 'span',
6854                 html : this.html || ''
6855             };
6856
6857             a.cn.push(span);
6858             
6859         }
6860         
6861         if (this.badge !== '') {
6862             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6863         }
6864         
6865         if (this.menu) {
6866             
6867             if(this.showArrow){
6868                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6869             }
6870             
6871             a.cls += ' dropdown-toggle treeview' ;
6872         }
6873         
6874         return cfg;
6875     },
6876     
6877     initEvents : function()
6878     { 
6879         if (typeof (this.menu) != 'undefined') {
6880             this.menu.parentType = this.xtype;
6881             this.menu.triggerEl = this.el;
6882             this.menu = this.addxtype(Roo.apply({}, this.menu));
6883         }
6884         
6885         this.el.on('click', this.onClick, this);
6886         
6887         if(this.badge !== ''){
6888             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6889         }
6890         
6891     },
6892     
6893     onClick : function(e)
6894     {
6895         if(this.disabled){
6896             e.preventDefault();
6897             return;
6898         }
6899         
6900         if(this.preventDefault){
6901             e.preventDefault();
6902         }
6903         
6904         this.fireEvent('click', this, e);
6905     },
6906     
6907     disable : function()
6908     {
6909         this.setDisabled(true);
6910     },
6911     
6912     enable : function()
6913     {
6914         this.setDisabled(false);
6915     },
6916     
6917     setDisabled : function(state)
6918     {
6919         if(this.disabled == state){
6920             return;
6921         }
6922         
6923         this.disabled = state;
6924         
6925         if (state) {
6926             this.el.addClass('disabled');
6927             return;
6928         }
6929         
6930         this.el.removeClass('disabled');
6931         
6932         return;
6933     },
6934     
6935     setActive : function(state)
6936     {
6937         if(this.active == state){
6938             return;
6939         }
6940         
6941         this.active = state;
6942         
6943         if (state) {
6944             this.el.addClass('active');
6945             return;
6946         }
6947         
6948         this.el.removeClass('active');
6949         
6950         return;
6951     },
6952     
6953     isActive: function () 
6954     {
6955         return this.active;
6956     },
6957     
6958     setBadge : function(str)
6959     {
6960         if(!this.badgeEl){
6961             return;
6962         }
6963         
6964         this.badgeEl.dom.innerHTML = str;
6965     }
6966     
6967    
6968      
6969  
6970 });
6971  
6972
6973  /*
6974  * - LGPL
6975  *
6976  * nav progress bar
6977  * 
6978  */
6979
6980 /**
6981  * @class Roo.bootstrap.nav.ProgressBar
6982  * @extends Roo.bootstrap.Component
6983  * @children Roo.bootstrap.nav.ProgressBarItem
6984  * Bootstrap NavProgressBar class
6985  * 
6986  * @constructor
6987  * Create a new nav progress bar - a bar indicating step along a process
6988  * @param {Object} config The config object
6989  */
6990
6991 Roo.bootstrap.nav.ProgressBar = function(config){
6992     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6993
6994     this.bullets = this.bullets || [];
6995    
6996 //    Roo.bootstrap.nav.ProgressBar.register(this);
6997      this.addEvents({
6998         /**
6999              * @event changed
7000              * Fires when the active item changes
7001              * @param {Roo.bootstrap.nav.ProgressBar} this
7002              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7003              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
7004          */
7005         'changed': true
7006      });
7007     
7008 };
7009
7010 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7011     /**
7012      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7013      * Bullets for the Nav Progress bar for the toolbar
7014      */
7015     bullets : [],
7016     barItems : [],
7017     
7018     getAutoCreate : function()
7019     {
7020         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7021         
7022         cfg = {
7023             tag : 'div',
7024             cls : 'roo-navigation-bar-group',
7025             cn : [
7026                 {
7027                     tag : 'div',
7028                     cls : 'roo-navigation-top-bar'
7029                 },
7030                 {
7031                     tag : 'div',
7032                     cls : 'roo-navigation-bullets-bar',
7033                     cn : [
7034                         {
7035                             tag : 'ul',
7036                             cls : 'roo-navigation-bar'
7037                         }
7038                     ]
7039                 },
7040                 
7041                 {
7042                     tag : 'div',
7043                     cls : 'roo-navigation-bottom-bar'
7044                 }
7045             ]
7046             
7047         };
7048         
7049         return cfg;
7050         
7051     },
7052     
7053     initEvents: function() 
7054     {
7055         
7056     },
7057     
7058     onRender : function(ct, position) 
7059     {
7060         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7061         
7062         if(this.bullets.length){
7063             Roo.each(this.bullets, function(b){
7064                this.addItem(b);
7065             }, this);
7066         }
7067         
7068         this.format();
7069         
7070     },
7071     
7072     addItem : function(cfg)
7073     {
7074         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7075         
7076         item.parentId = this.id;
7077         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7078         
7079         if(cfg.html){
7080             var top = new Roo.bootstrap.Element({
7081                 tag : 'div',
7082                 cls : 'roo-navigation-bar-text'
7083             });
7084             
7085             var bottom = new Roo.bootstrap.Element({
7086                 tag : 'div',
7087                 cls : 'roo-navigation-bar-text'
7088             });
7089             
7090             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7091             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7092             
7093             var topText = new Roo.bootstrap.Element({
7094                 tag : 'span',
7095                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7096             });
7097             
7098             var bottomText = new Roo.bootstrap.Element({
7099                 tag : 'span',
7100                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7101             });
7102             
7103             topText.onRender(top.el, null);
7104             bottomText.onRender(bottom.el, null);
7105             
7106             item.topEl = top;
7107             item.bottomEl = bottom;
7108         }
7109         
7110         this.barItems.push(item);
7111         
7112         return item;
7113     },
7114     
7115     getActive : function()
7116     {
7117         var active = false;
7118         
7119         Roo.each(this.barItems, function(v){
7120             
7121             if (!v.isActive()) {
7122                 return;
7123             }
7124             
7125             active = v;
7126             return false;
7127             
7128         });
7129         
7130         return active;
7131     },
7132     
7133     setActiveItem : function(item)
7134     {
7135         var prev = false;
7136         
7137         Roo.each(this.barItems, function(v){
7138             if (v.rid == item.rid) {
7139                 return ;
7140             }
7141             
7142             if (v.isActive()) {
7143                 v.setActive(false);
7144                 prev = v;
7145             }
7146         });
7147
7148         item.setActive(true);
7149         
7150         this.fireEvent('changed', this, item, prev);
7151     },
7152     
7153     getBarItem: function(rid)
7154     {
7155         var ret = false;
7156         
7157         Roo.each(this.barItems, function(e) {
7158             if (e.rid != rid) {
7159                 return;
7160             }
7161             
7162             ret =  e;
7163             return false;
7164         });
7165         
7166         return ret;
7167     },
7168     
7169     indexOfItem : function(item)
7170     {
7171         var index = false;
7172         
7173         Roo.each(this.barItems, function(v, i){
7174             
7175             if (v.rid != item.rid) {
7176                 return;
7177             }
7178             
7179             index = i;
7180             return false
7181         });
7182         
7183         return index;
7184     },
7185     
7186     setActiveNext : function()
7187     {
7188         var i = this.indexOfItem(this.getActive());
7189         
7190         if (i > this.barItems.length) {
7191             return;
7192         }
7193         
7194         this.setActiveItem(this.barItems[i+1]);
7195     },
7196     
7197     setActivePrev : function()
7198     {
7199         var i = this.indexOfItem(this.getActive());
7200         
7201         if (i  < 1) {
7202             return;
7203         }
7204         
7205         this.setActiveItem(this.barItems[i-1]);
7206     },
7207     
7208     format : function()
7209     {
7210         if(!this.barItems.length){
7211             return;
7212         }
7213      
7214         var width = 100 / this.barItems.length;
7215         
7216         Roo.each(this.barItems, function(i){
7217             i.el.setStyle('width', width + '%');
7218             i.topEl.el.setStyle('width', width + '%');
7219             i.bottomEl.el.setStyle('width', width + '%');
7220         }, this);
7221         
7222     }
7223     
7224 });
7225 /*
7226  * - LGPL
7227  *
7228  * Nav Progress Item
7229  * 
7230  */
7231
7232 /**
7233  * @class Roo.bootstrap.nav.ProgressBarItem
7234  * @extends Roo.bootstrap.Component
7235  * Bootstrap NavProgressBarItem class
7236  * @cfg {String} rid the reference id
7237  * @cfg {Boolean} active (true|false) Is item active default false
7238  * @cfg {Boolean} disabled (true|false) Is item active default false
7239  * @cfg {String} html
7240  * @cfg {String} position (top|bottom) text position default bottom
7241  * @cfg {String} icon show icon instead of number
7242  * 
7243  * @constructor
7244  * Create a new NavProgressBarItem
7245  * @param {Object} config The config object
7246  */
7247 Roo.bootstrap.nav.ProgressBarItem = function(config){
7248     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7249     this.addEvents({
7250         // raw events
7251         /**
7252          * @event click
7253          * The raw click event for the entire grid.
7254          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7255          * @param {Roo.EventObject} e
7256          */
7257         "click" : true
7258     });
7259    
7260 };
7261
7262 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7263     
7264     rid : '',
7265     active : false,
7266     disabled : false,
7267     html : '',
7268     position : 'bottom',
7269     icon : false,
7270     
7271     getAutoCreate : function()
7272     {
7273         var iconCls = 'roo-navigation-bar-item-icon';
7274         
7275         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7276         
7277         var cfg = {
7278             tag: 'li',
7279             cls: 'roo-navigation-bar-item',
7280             cn : [
7281                 {
7282                     tag : 'i',
7283                     cls : iconCls
7284                 }
7285             ]
7286         };
7287         
7288         if(this.active){
7289             cfg.cls += ' active';
7290         }
7291         if(this.disabled){
7292             cfg.cls += ' disabled';
7293         }
7294         
7295         return cfg;
7296     },
7297     
7298     disable : function()
7299     {
7300         this.setDisabled(true);
7301     },
7302     
7303     enable : function()
7304     {
7305         this.setDisabled(false);
7306     },
7307     
7308     initEvents: function() 
7309     {
7310         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7311         
7312         this.iconEl.on('click', this.onClick, this);
7313     },
7314     
7315     onClick : function(e)
7316     {
7317         e.preventDefault();
7318         
7319         if(this.disabled){
7320             return;
7321         }
7322         
7323         if(this.fireEvent('click', this, e) === false){
7324             return;
7325         };
7326         
7327         this.parent().setActiveItem(this);
7328     },
7329     
7330     isActive: function () 
7331     {
7332         return this.active;
7333     },
7334     
7335     setActive : function(state)
7336     {
7337         if(this.active == state){
7338             return;
7339         }
7340         
7341         this.active = state;
7342         
7343         if (state) {
7344             this.el.addClass('active');
7345             return;
7346         }
7347         
7348         this.el.removeClass('active');
7349         
7350         return;
7351     },
7352     
7353     setDisabled : function(state)
7354     {
7355         if(this.disabled == state){
7356             return;
7357         }
7358         
7359         this.disabled = state;
7360         
7361         if (state) {
7362             this.el.addClass('disabled');
7363             return;
7364         }
7365         
7366         this.el.removeClass('disabled');
7367     },
7368     
7369     tooltipEl : function()
7370     {
7371         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7372     }
7373 });
7374  
7375
7376  /*
7377  * - LGPL
7378  *
7379  *  Breadcrumb Nav
7380  * 
7381  */
7382 Roo.namespace('Roo.bootstrap.breadcrumb');
7383
7384
7385 /**
7386  * @class Roo.bootstrap.breadcrumb.Nav
7387  * @extends Roo.bootstrap.Component
7388  * Bootstrap Breadcrumb Nav Class
7389  *  
7390  * @children Roo.bootstrap.breadcrumb.Item
7391  * 
7392  * @constructor
7393  * Create a new breadcrumb.Nav
7394  * @param {Object} config The config object
7395  */
7396
7397
7398 Roo.bootstrap.breadcrumb.Nav = function(config){
7399     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7400     
7401     
7402 };
7403
7404 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7405     
7406     getAutoCreate : function()
7407     {
7408
7409         var cfg = {
7410             tag: 'nav',
7411             cn : [
7412                 {
7413                     tag : 'ol',
7414                     cls : 'breadcrumb'
7415                 }
7416             ]
7417             
7418         };
7419           
7420         return cfg;
7421     },
7422     
7423     initEvents: function()
7424     {
7425         this.olEl = this.el.select('ol',true).first();    
7426     },
7427     getChildContainer : function()
7428     {
7429         return this.olEl;  
7430     }
7431     
7432 });
7433
7434  /*
7435  * - LGPL
7436  *
7437  *  Breadcrumb Item
7438  * 
7439  */
7440
7441
7442 /**
7443  * @class Roo.bootstrap.breadcrumb.Nav
7444  * @extends Roo.bootstrap.Component
7445  * @children Roo.bootstrap.Component
7446  * @parent Roo.bootstrap.breadcrumb.Nav
7447  * Bootstrap Breadcrumb Nav Class
7448  *  
7449  * 
7450  * @cfg {String} html the content of the link.
7451  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7452  * @cfg {Boolean} active is it active
7453
7454  * 
7455  * @constructor
7456  * Create a new breadcrumb.Nav
7457  * @param {Object} config The config object
7458  */
7459
7460 Roo.bootstrap.breadcrumb.Item = function(config){
7461     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7462     this.addEvents({
7463         // img events
7464         /**
7465          * @event click
7466          * The img click event for the img.
7467          * @param {Roo.EventObject} e
7468          */
7469         "click" : true
7470     });
7471     
7472 };
7473
7474 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7475     
7476     href: false,
7477     html : '',
7478     
7479     getAutoCreate : function()
7480     {
7481
7482         var cfg = {
7483             tag: 'li',
7484             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7485         };
7486         if (this.href !== false) {
7487             cfg.cn = [{
7488                 tag : 'a',
7489                 href : this.href,
7490                 html : this.html
7491             }];
7492         } else {
7493             cfg.html = this.html;
7494         }
7495         
7496         return cfg;
7497     },
7498     
7499     initEvents: function()
7500     {
7501         if (this.href) {
7502             this.el.select('a', true).first().on('click',this.onClick, this)
7503         }
7504         
7505     },
7506     onClick : function(e)
7507     {
7508         e.preventDefault();
7509         this.fireEvent('click',this,  e);
7510     }
7511     
7512 });
7513
7514  /*
7515  * - LGPL
7516  *
7517  * row
7518  * 
7519  */
7520
7521 /**
7522  * @class Roo.bootstrap.Row
7523  * @extends Roo.bootstrap.Component
7524  * @children Roo.bootstrap.Component
7525  * Bootstrap Row class (contains columns...)
7526  * 
7527  * @constructor
7528  * Create a new Row
7529  * @param {Object} config The config object
7530  */
7531
7532 Roo.bootstrap.Row = function(config){
7533     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7534 };
7535
7536 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7537     
7538     getAutoCreate : function(){
7539        return {
7540             cls: 'row clearfix'
7541        };
7542     }
7543     
7544     
7545 });
7546
7547  
7548
7549  /*
7550  * - LGPL
7551  *
7552  * pagination
7553  * 
7554  */
7555
7556 /**
7557  * @class Roo.bootstrap.Pagination
7558  * @extends Roo.bootstrap.Component
7559  * @children Roo.bootstrap.Pagination
7560  * Bootstrap Pagination class
7561  * 
7562  * @cfg {String} size (xs|sm|md|lg|xl)
7563  * @cfg {Boolean} inverse 
7564  * 
7565  * @constructor
7566  * Create a new Pagination
7567  * @param {Object} config The config object
7568  */
7569
7570 Roo.bootstrap.Pagination = function(config){
7571     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7572 };
7573
7574 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7575     
7576     cls: false,
7577     size: false,
7578     inverse: false,
7579     
7580     getAutoCreate : function(){
7581         var cfg = {
7582             tag: 'ul',
7583                 cls: 'pagination'
7584         };
7585         if (this.inverse) {
7586             cfg.cls += ' inverse';
7587         }
7588         if (this.html) {
7589             cfg.html=this.html;
7590         }
7591         if (this.cls) {
7592             cfg.cls += " " + this.cls;
7593         }
7594         return cfg;
7595     }
7596    
7597 });
7598
7599  
7600
7601  /*
7602  * - LGPL
7603  *
7604  * Pagination item
7605  * 
7606  */
7607
7608
7609 /**
7610  * @class Roo.bootstrap.PaginationItem
7611  * @extends Roo.bootstrap.Component
7612  * Bootstrap PaginationItem class
7613  * @cfg {String} html text
7614  * @cfg {String} href the link
7615  * @cfg {Boolean} preventDefault (true | false) default true
7616  * @cfg {Boolean} active (true | false) default false
7617  * @cfg {Boolean} disabled default false
7618  * 
7619  * 
7620  * @constructor
7621  * Create a new PaginationItem
7622  * @param {Object} config The config object
7623  */
7624
7625
7626 Roo.bootstrap.PaginationItem = function(config){
7627     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7628     this.addEvents({
7629         // raw events
7630         /**
7631          * @event click
7632          * The raw click event for the entire grid.
7633          * @param {Roo.EventObject} e
7634          */
7635         "click" : true
7636     });
7637 };
7638
7639 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7640     
7641     href : false,
7642     html : false,
7643     preventDefault: true,
7644     active : false,
7645     cls : false,
7646     disabled: false,
7647     
7648     getAutoCreate : function(){
7649         var cfg= {
7650             tag: 'li',
7651             cn: [
7652                 {
7653                     tag : 'a',
7654                     href : this.href ? this.href : '#',
7655                     html : this.html ? this.html : ''
7656                 }
7657             ]
7658         };
7659         
7660         if(this.cls){
7661             cfg.cls = this.cls;
7662         }
7663         
7664         if(this.disabled){
7665             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7666         }
7667         
7668         if(this.active){
7669             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7670         }
7671         
7672         return cfg;
7673     },
7674     
7675     initEvents: function() {
7676         
7677         this.el.on('click', this.onClick, this);
7678         
7679     },
7680     onClick : function(e)
7681     {
7682         Roo.log('PaginationItem on click ');
7683         if(this.preventDefault){
7684             e.preventDefault();
7685         }
7686         
7687         if(this.disabled){
7688             return;
7689         }
7690         
7691         this.fireEvent('click', this, e);
7692     }
7693    
7694 });
7695
7696  
7697
7698  /*
7699  * - LGPL
7700  *
7701  * slider
7702  * 
7703  */
7704
7705
7706 /**
7707  * @class Roo.bootstrap.Slider
7708  * @extends Roo.bootstrap.Component
7709  * Bootstrap Slider class
7710  *    
7711  * @constructor
7712  * Create a new Slider
7713  * @param {Object} config The config object
7714  */
7715
7716 Roo.bootstrap.Slider = function(config){
7717     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7718 };
7719
7720 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7721     
7722     getAutoCreate : function(){
7723         
7724         var cfg = {
7725             tag: 'div',
7726             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7727             cn: [
7728                 {
7729                     tag: 'a',
7730                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7731                 }
7732             ]
7733         };
7734         
7735         return cfg;
7736     }
7737    
7738 });
7739
7740  /*
7741  * Based on:
7742  * Ext JS Library 1.1.1
7743  * Copyright(c) 2006-2007, Ext JS, LLC.
7744  *
7745  * Originally Released Under LGPL - original licence link has changed is not relivant.
7746  *
7747  * Fork - LGPL
7748  * <script type="text/javascript">
7749  */
7750  /**
7751  * @extends Roo.dd.DDProxy
7752  * @class Roo.grid.SplitDragZone
7753  * Support for Column Header resizing
7754  * @constructor
7755  * @param {Object} config
7756  */
7757 // private
7758 // This is a support class used internally by the Grid components
7759 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7760     this.grid = grid;
7761     this.view = grid.getView();
7762     this.proxy = this.view.resizeProxy;
7763     Roo.grid.SplitDragZone.superclass.constructor.call(
7764         this,
7765         hd, // ID
7766         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7767         {  // CONFIG
7768             dragElId : Roo.id(this.proxy.dom),
7769             resizeFrame:false
7770         }
7771     );
7772     
7773     this.setHandleElId(Roo.id(hd));
7774     if (hd2 !== false) {
7775         this.setOuterHandleElId(Roo.id(hd2));
7776     }
7777     
7778     this.scroll = false;
7779 };
7780 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7781     fly: Roo.Element.fly,
7782
7783     b4StartDrag : function(x, y){
7784         this.view.headersDisabled = true;
7785         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7786                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7787         );
7788         this.proxy.setHeight(h);
7789         
7790         // for old system colWidth really stored the actual width?
7791         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7792         // which in reality did not work.. - it worked only for fixed sizes
7793         // for resizable we need to use actual sizes.
7794         var w = this.cm.getColumnWidth(this.cellIndex);
7795         if (!this.view.mainWrap) {
7796             // bootstrap.
7797             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7798         }
7799         
7800         
7801         
7802         // this was w-this.grid.minColumnWidth;
7803         // doesnt really make sense? - w = thie curren width or the rendered one?
7804         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7805         this.resetConstraints();
7806         this.setXConstraint(minw, 1000);
7807         this.setYConstraint(0, 0);
7808         this.minX = x - minw;
7809         this.maxX = x + 1000;
7810         this.startPos = x;
7811         if (!this.view.mainWrap) { // this is Bootstrap code..
7812             this.getDragEl().style.display='block';
7813         }
7814         
7815         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7816     },
7817
7818
7819     handleMouseDown : function(e){
7820         ev = Roo.EventObject.setEvent(e);
7821         var t = this.fly(ev.getTarget());
7822         if(t.hasClass("x-grid-split")){
7823             this.cellIndex = this.view.getCellIndex(t.dom);
7824             this.split = t.dom;
7825             this.cm = this.grid.colModel;
7826             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7827                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7828             }
7829         }
7830     },
7831
7832     endDrag : function(e){
7833         this.view.headersDisabled = false;
7834         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7835         var diff = endX - this.startPos;
7836         // 
7837         var w = this.cm.getColumnWidth(this.cellIndex);
7838         if (!this.view.mainWrap) {
7839             w = 0;
7840         }
7841         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7842     },
7843
7844     autoOffset : function(){
7845         this.setDelta(0,0);
7846     }
7847 });/*
7848  * Based on:
7849  * Ext JS Library 1.1.1
7850  * Copyright(c) 2006-2007, Ext JS, LLC.
7851  *
7852  * Originally Released Under LGPL - original licence link has changed is not relivant.
7853  *
7854  * Fork - LGPL
7855  * <script type="text/javascript">
7856  */
7857
7858 /**
7859  * @class Roo.grid.AbstractSelectionModel
7860  * @extends Roo.util.Observable
7861  * @abstract
7862  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7863  * implemented by descendant classes.  This class should not be directly instantiated.
7864  * @constructor
7865  */
7866 Roo.grid.AbstractSelectionModel = function(){
7867     this.locked = false;
7868     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7869 };
7870
7871 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7872     /** @ignore Called by the grid automatically. Do not call directly. */
7873     init : function(grid){
7874         this.grid = grid;
7875         this.initEvents();
7876     },
7877
7878     /**
7879      * Locks the selections.
7880      */
7881     lock : function(){
7882         this.locked = true;
7883     },
7884
7885     /**
7886      * Unlocks the selections.
7887      */
7888     unlock : function(){
7889         this.locked = false;
7890     },
7891
7892     /**
7893      * Returns true if the selections are locked.
7894      * @return {Boolean}
7895      */
7896     isLocked : function(){
7897         return this.locked;
7898     }
7899 });/*
7900  * Based on:
7901  * Ext JS Library 1.1.1
7902  * Copyright(c) 2006-2007, Ext JS, LLC.
7903  *
7904  * Originally Released Under LGPL - original licence link has changed is not relivant.
7905  *
7906  * Fork - LGPL
7907  * <script type="text/javascript">
7908  */
7909 /**
7910  * @extends Roo.grid.AbstractSelectionModel
7911  * @class Roo.grid.RowSelectionModel
7912  * The default SelectionModel used by {@link Roo.grid.Grid}.
7913  * It supports multiple selections and keyboard selection/navigation. 
7914  * @constructor
7915  * @param {Object} config
7916  */
7917 Roo.grid.RowSelectionModel = function(config){
7918     Roo.apply(this, config);
7919     this.selections = new Roo.util.MixedCollection(false, function(o){
7920         return o.id;
7921     });
7922
7923     this.last = false;
7924     this.lastActive = false;
7925
7926     this.addEvents({
7927         /**
7928         * @event selectionchange
7929         * Fires when the selection changes
7930         * @param {SelectionModel} this
7931         */
7932        "selectionchange" : true,
7933        /**
7934         * @event afterselectionchange
7935         * Fires after the selection changes (eg. by key press or clicking)
7936         * @param {SelectionModel} this
7937         */
7938        "afterselectionchange" : true,
7939        /**
7940         * @event beforerowselect
7941         * Fires when a row is selected being selected, return false to cancel.
7942         * @param {SelectionModel} this
7943         * @param {Number} rowIndex The selected index
7944         * @param {Boolean} keepExisting False if other selections will be cleared
7945         */
7946        "beforerowselect" : true,
7947        /**
7948         * @event rowselect
7949         * Fires when a row is selected.
7950         * @param {SelectionModel} this
7951         * @param {Number} rowIndex The selected index
7952         * @param {Roo.data.Record} r The record
7953         */
7954        "rowselect" : true,
7955        /**
7956         * @event rowdeselect
7957         * Fires when a row is deselected.
7958         * @param {SelectionModel} this
7959         * @param {Number} rowIndex The selected index
7960         */
7961         "rowdeselect" : true
7962     });
7963     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7964     this.locked = false;
7965 };
7966
7967 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7968     /**
7969      * @cfg {Boolean} singleSelect
7970      * True to allow selection of only one row at a time (defaults to false)
7971      */
7972     singleSelect : false,
7973
7974     // private
7975     initEvents : function(){
7976
7977         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7978             this.grid.on("mousedown", this.handleMouseDown, this);
7979         }else{ // allow click to work like normal
7980             this.grid.on("rowclick", this.handleDragableRowClick, this);
7981         }
7982         // bootstrap does not have a view..
7983         var view = this.grid.view ? this.grid.view : this.grid;
7984         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7985             "up" : function(e){
7986                 if(!e.shiftKey){
7987                     this.selectPrevious(e.shiftKey);
7988                 }else if(this.last !== false && this.lastActive !== false){
7989                     var last = this.last;
7990                     this.selectRange(this.last,  this.lastActive-1);
7991                     view.focusRow(this.lastActive);
7992                     if(last !== false){
7993                         this.last = last;
7994                     }
7995                 }else{
7996                     this.selectFirstRow();
7997                 }
7998                 this.fireEvent("afterselectionchange", this);
7999             },
8000             "down" : function(e){
8001                 if(!e.shiftKey){
8002                     this.selectNext(e.shiftKey);
8003                 }else if(this.last !== false && this.lastActive !== false){
8004                     var last = this.last;
8005                     this.selectRange(this.last,  this.lastActive+1);
8006                     view.focusRow(this.lastActive);
8007                     if(last !== false){
8008                         this.last = last;
8009                     }
8010                 }else{
8011                     this.selectFirstRow();
8012                 }
8013                 this.fireEvent("afterselectionchange", this);
8014             },
8015             scope: this
8016         });
8017
8018          
8019         view.on("refresh", this.onRefresh, this);
8020         view.on("rowupdated", this.onRowUpdated, this);
8021         view.on("rowremoved", this.onRemove, this);
8022     },
8023
8024     // private
8025     onRefresh : function(){
8026         var ds = this.grid.ds, i, v = this.grid.view;
8027         var s = this.selections;
8028         s.each(function(r){
8029             if((i = ds.indexOfId(r.id)) != -1){
8030                 v.onRowSelect(i);
8031                 s.add(ds.getAt(i)); // updating the selection relate data
8032             }else{
8033                 s.remove(r);
8034             }
8035         });
8036     },
8037
8038     // private
8039     onRemove : function(v, index, r){
8040         this.selections.remove(r);
8041     },
8042
8043     // private
8044     onRowUpdated : function(v, index, r){
8045         if(this.isSelected(r)){
8046             v.onRowSelect(index);
8047         }
8048     },
8049
8050     /**
8051      * Select records.
8052      * @param {Array} records The records to select
8053      * @param {Boolean} keepExisting (optional) True to keep existing selections
8054      */
8055     selectRecords : function(records, keepExisting){
8056         if(!keepExisting){
8057             this.clearSelections();
8058         }
8059         var ds = this.grid.ds;
8060         for(var i = 0, len = records.length; i < len; i++){
8061             this.selectRow(ds.indexOf(records[i]), true);
8062         }
8063     },
8064
8065     /**
8066      * Gets the number of selected rows.
8067      * @return {Number}
8068      */
8069     getCount : function(){
8070         return this.selections.length;
8071     },
8072
8073     /**
8074      * Selects the first row in the grid.
8075      */
8076     selectFirstRow : function(){
8077         this.selectRow(0);
8078     },
8079
8080     /**
8081      * Select the last row.
8082      * @param {Boolean} keepExisting (optional) True to keep existing selections
8083      */
8084     selectLastRow : function(keepExisting){
8085         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8086     },
8087
8088     /**
8089      * Selects the row immediately following the last selected row.
8090      * @param {Boolean} keepExisting (optional) True to keep existing selections
8091      */
8092     selectNext : function(keepExisting){
8093         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8094             this.selectRow(this.last+1, keepExisting);
8095             var view = this.grid.view ? this.grid.view : this.grid;
8096             view.focusRow(this.last);
8097         }
8098     },
8099
8100     /**
8101      * Selects the row that precedes the last selected row.
8102      * @param {Boolean} keepExisting (optional) True to keep existing selections
8103      */
8104     selectPrevious : function(keepExisting){
8105         if(this.last){
8106             this.selectRow(this.last-1, keepExisting);
8107             var view = this.grid.view ? this.grid.view : this.grid;
8108             view.focusRow(this.last);
8109         }
8110     },
8111
8112     /**
8113      * Returns the selected records
8114      * @return {Array} Array of selected records
8115      */
8116     getSelections : function(){
8117         return [].concat(this.selections.items);
8118     },
8119
8120     /**
8121      * Returns the first selected record.
8122      * @return {Record}
8123      */
8124     getSelected : function(){
8125         return this.selections.itemAt(0);
8126     },
8127
8128
8129     /**
8130      * Clears all selections.
8131      */
8132     clearSelections : function(fast){
8133         if(this.locked) {
8134             return;
8135         }
8136         if(fast !== true){
8137             var ds = this.grid.ds;
8138             var s = this.selections;
8139             s.each(function(r){
8140                 this.deselectRow(ds.indexOfId(r.id));
8141             }, this);
8142             s.clear();
8143         }else{
8144             this.selections.clear();
8145         }
8146         this.last = false;
8147     },
8148
8149
8150     /**
8151      * Selects all rows.
8152      */
8153     selectAll : function(){
8154         if(this.locked) {
8155             return;
8156         }
8157         this.selections.clear();
8158         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8159             this.selectRow(i, true);
8160         }
8161     },
8162
8163     /**
8164      * Returns True if there is a selection.
8165      * @return {Boolean}
8166      */
8167     hasSelection : function(){
8168         return this.selections.length > 0;
8169     },
8170
8171     /**
8172      * Returns True if the specified row is selected.
8173      * @param {Number/Record} record The record or index of the record to check
8174      * @return {Boolean}
8175      */
8176     isSelected : function(index){
8177         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8178         return (r && this.selections.key(r.id) ? true : false);
8179     },
8180
8181     /**
8182      * Returns True if the specified record id is selected.
8183      * @param {String} id The id of record to check
8184      * @return {Boolean}
8185      */
8186     isIdSelected : function(id){
8187         return (this.selections.key(id) ? true : false);
8188     },
8189
8190     // private
8191     handleMouseDown : function(e, t)
8192     {
8193         var view = this.grid.view ? this.grid.view : this.grid;
8194         var rowIndex;
8195         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8196             return;
8197         };
8198         if(e.shiftKey && this.last !== false){
8199             var last = this.last;
8200             this.selectRange(last, rowIndex, e.ctrlKey);
8201             this.last = last; // reset the last
8202             view.focusRow(rowIndex);
8203         }else{
8204             var isSelected = this.isSelected(rowIndex);
8205             if(e.button !== 0 && isSelected){
8206                 view.focusRow(rowIndex);
8207             }else if(e.ctrlKey && isSelected){
8208                 this.deselectRow(rowIndex);
8209             }else if(!isSelected){
8210                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8211                 view.focusRow(rowIndex);
8212             }
8213         }
8214         this.fireEvent("afterselectionchange", this);
8215     },
8216     // private
8217     handleDragableRowClick :  function(grid, rowIndex, e) 
8218     {
8219         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8220             this.selectRow(rowIndex, false);
8221             var view = this.grid.view ? this.grid.view : this.grid;
8222             view.focusRow(rowIndex);
8223              this.fireEvent("afterselectionchange", this);
8224         }
8225     },
8226     
8227     /**
8228      * Selects multiple rows.
8229      * @param {Array} rows Array of the indexes of the row to select
8230      * @param {Boolean} keepExisting (optional) True to keep existing selections
8231      */
8232     selectRows : function(rows, keepExisting){
8233         if(!keepExisting){
8234             this.clearSelections();
8235         }
8236         for(var i = 0, len = rows.length; i < len; i++){
8237             this.selectRow(rows[i], true);
8238         }
8239     },
8240
8241     /**
8242      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8243      * @param {Number} startRow The index of the first row in the range
8244      * @param {Number} endRow The index of the last row in the range
8245      * @param {Boolean} keepExisting (optional) True to retain existing selections
8246      */
8247     selectRange : function(startRow, endRow, keepExisting){
8248         if(this.locked) {
8249             return;
8250         }
8251         if(!keepExisting){
8252             this.clearSelections();
8253         }
8254         if(startRow <= endRow){
8255             for(var i = startRow; i <= endRow; i++){
8256                 this.selectRow(i, true);
8257             }
8258         }else{
8259             for(var i = startRow; i >= endRow; i--){
8260                 this.selectRow(i, true);
8261             }
8262         }
8263     },
8264
8265     /**
8266      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8267      * @param {Number} startRow The index of the first row in the range
8268      * @param {Number} endRow The index of the last row in the range
8269      */
8270     deselectRange : function(startRow, endRow, preventViewNotify){
8271         if(this.locked) {
8272             return;
8273         }
8274         for(var i = startRow; i <= endRow; i++){
8275             this.deselectRow(i, preventViewNotify);
8276         }
8277     },
8278
8279     /**
8280      * Selects a row.
8281      * @param {Number} row The index of the row to select
8282      * @param {Boolean} keepExisting (optional) True to keep existing selections
8283      */
8284     selectRow : function(index, keepExisting, preventViewNotify){
8285         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8286             return;
8287         }
8288         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8289             if(!keepExisting || this.singleSelect){
8290                 this.clearSelections();
8291             }
8292             var r = this.grid.ds.getAt(index);
8293             this.selections.add(r);
8294             this.last = this.lastActive = index;
8295             if(!preventViewNotify){
8296                 var view = this.grid.view ? this.grid.view : this.grid;
8297                 view.onRowSelect(index);
8298             }
8299             this.fireEvent("rowselect", this, index, r);
8300             this.fireEvent("selectionchange", this);
8301         }
8302     },
8303
8304     /**
8305      * Deselects a row.
8306      * @param {Number} row The index of the row to deselect
8307      */
8308     deselectRow : function(index, preventViewNotify){
8309         if(this.locked) {
8310             return;
8311         }
8312         if(this.last == index){
8313             this.last = false;
8314         }
8315         if(this.lastActive == index){
8316             this.lastActive = false;
8317         }
8318         var r = this.grid.ds.getAt(index);
8319         this.selections.remove(r);
8320         if(!preventViewNotify){
8321             var view = this.grid.view ? this.grid.view : this.grid;
8322             view.onRowDeselect(index);
8323         }
8324         this.fireEvent("rowdeselect", this, index);
8325         this.fireEvent("selectionchange", this);
8326     },
8327
8328     // private
8329     restoreLast : function(){
8330         if(this._last){
8331             this.last = this._last;
8332         }
8333     },
8334
8335     // private
8336     acceptsNav : function(row, col, cm){
8337         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8338     },
8339
8340     // private
8341     onEditorKey : function(field, e){
8342         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8343         if(k == e.TAB){
8344             e.stopEvent();
8345             ed.completeEdit();
8346             if(e.shiftKey){
8347                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8348             }else{
8349                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8350             }
8351         }else if(k == e.ENTER && !e.ctrlKey){
8352             e.stopEvent();
8353             ed.completeEdit();
8354             if(e.shiftKey){
8355                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8356             }else{
8357                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8358             }
8359         }else if(k == e.ESC){
8360             ed.cancelEdit();
8361         }
8362         if(newCell){
8363             g.startEditing(newCell[0], newCell[1]);
8364         }
8365     }
8366 });/*
8367  * Based on:
8368  * Ext JS Library 1.1.1
8369  * Copyright(c) 2006-2007, Ext JS, LLC.
8370  *
8371  * Originally Released Under LGPL - original licence link has changed is not relivant.
8372  *
8373  * Fork - LGPL
8374  * <script type="text/javascript">
8375  */
8376  
8377
8378 /**
8379  * @class Roo.grid.ColumnModel
8380  * @extends Roo.util.Observable
8381  * This is the default implementation of a ColumnModel used by the Grid. It defines
8382  * the columns in the grid.
8383  * <br>Usage:<br>
8384  <pre><code>
8385  var colModel = new Roo.grid.ColumnModel([
8386         {header: "Ticker", width: 60, sortable: true, locked: true},
8387         {header: "Company Name", width: 150, sortable: true},
8388         {header: "Market Cap.", width: 100, sortable: true},
8389         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8390         {header: "Employees", width: 100, sortable: true, resizable: false}
8391  ]);
8392  </code></pre>
8393  * <p>
8394  
8395  * The config options listed for this class are options which may appear in each
8396  * individual column definition.
8397  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8398  * @constructor
8399  * @param {Object} config An Array of column config objects. See this class's
8400  * config objects for details.
8401 */
8402 Roo.grid.ColumnModel = function(config){
8403         /**
8404      * The config passed into the constructor
8405      */
8406     this.config = []; //config;
8407     this.lookup = {};
8408
8409     // if no id, create one
8410     // if the column does not have a dataIndex mapping,
8411     // map it to the order it is in the config
8412     for(var i = 0, len = config.length; i < len; i++){
8413         this.addColumn(config[i]);
8414         
8415     }
8416
8417     /**
8418      * The width of columns which have no width specified (defaults to 100)
8419      * @type Number
8420      */
8421     this.defaultWidth = 100;
8422
8423     /**
8424      * Default sortable of columns which have no sortable specified (defaults to false)
8425      * @type Boolean
8426      */
8427     this.defaultSortable = false;
8428
8429     this.addEvents({
8430         /**
8431              * @event widthchange
8432              * Fires when the width of a column changes.
8433              * @param {ColumnModel} this
8434              * @param {Number} columnIndex The column index
8435              * @param {Number} newWidth The new width
8436              */
8437             "widthchange": true,
8438         /**
8439              * @event headerchange
8440              * Fires when the text of a header changes.
8441              * @param {ColumnModel} this
8442              * @param {Number} columnIndex The column index
8443              * @param {Number} newText The new header text
8444              */
8445             "headerchange": true,
8446         /**
8447              * @event hiddenchange
8448              * Fires when a column is hidden or "unhidden".
8449              * @param {ColumnModel} this
8450              * @param {Number} columnIndex The column index
8451              * @param {Boolean} hidden true if hidden, false otherwise
8452              */
8453             "hiddenchange": true,
8454             /**
8455          * @event columnmoved
8456          * Fires when a column is moved.
8457          * @param {ColumnModel} this
8458          * @param {Number} oldIndex
8459          * @param {Number} newIndex
8460          */
8461         "columnmoved" : true,
8462         /**
8463          * @event columlockchange
8464          * Fires when a column's locked state is changed
8465          * @param {ColumnModel} this
8466          * @param {Number} colIndex
8467          * @param {Boolean} locked true if locked
8468          */
8469         "columnlockchange" : true
8470     });
8471     Roo.grid.ColumnModel.superclass.constructor.call(this);
8472 };
8473 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8474     /**
8475      * @cfg {String} header [required] The header text to display in the Grid view.
8476      */
8477         /**
8478      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8479      */
8480         /**
8481      * @cfg {String} smHeader Header at Bootsrap Small width
8482      */
8483         /**
8484      * @cfg {String} mdHeader Header at Bootsrap Medium width
8485      */
8486         /**
8487      * @cfg {String} lgHeader Header at Bootsrap Large width
8488      */
8489         /**
8490      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8491      */
8492     /**
8493      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8494      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8495      * specified, the column's index is used as an index into the Record's data Array.
8496      */
8497     /**
8498      * @cfg {Number} width  The initial width in pixels of the column. Using this
8499      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8500      */
8501     /**
8502      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8503      * Defaults to the value of the {@link #defaultSortable} property.
8504      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8505      */
8506     /**
8507      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8508      */
8509     /**
8510      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8511      */
8512     /**
8513      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8514      */
8515     /**
8516      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8517      */
8518     /**
8519      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8520      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8521      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8522      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8523      */
8524        /**
8525      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8526      */
8527     /**
8528      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8529      */
8530     /**
8531      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8532      */
8533     /**
8534      * @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)
8535      */
8536     /**
8537      * @cfg {String} tooltip mouse over tooltip text
8538      */
8539     /**
8540      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8541      */
8542     /**
8543      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8547      */
8548     /**
8549      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8550      */
8551         /**
8552      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8553      */
8554     /**
8555      * Returns the id of the column at the specified index.
8556      * @param {Number} index The column index
8557      * @return {String} the id
8558      */
8559     getColumnId : function(index){
8560         return this.config[index].id;
8561     },
8562
8563     /**
8564      * Returns the column for a specified id.
8565      * @param {String} id The column id
8566      * @return {Object} the column
8567      */
8568     getColumnById : function(id){
8569         return this.lookup[id];
8570     },
8571
8572     
8573     /**
8574      * Returns the column Object for a specified dataIndex.
8575      * @param {String} dataIndex The column dataIndex
8576      * @return {Object|Boolean} the column or false if not found
8577      */
8578     getColumnByDataIndex: function(dataIndex){
8579         var index = this.findColumnIndex(dataIndex);
8580         return index > -1 ? this.config[index] : false;
8581     },
8582     
8583     /**
8584      * Returns the index for a specified column id.
8585      * @param {String} id The column id
8586      * @return {Number} the index, or -1 if not found
8587      */
8588     getIndexById : function(id){
8589         for(var i = 0, len = this.config.length; i < len; i++){
8590             if(this.config[i].id == id){
8591                 return i;
8592             }
8593         }
8594         return -1;
8595     },
8596     
8597     /**
8598      * Returns the index for a specified column dataIndex.
8599      * @param {String} dataIndex The column dataIndex
8600      * @return {Number} the index, or -1 if not found
8601      */
8602     
8603     findColumnIndex : function(dataIndex){
8604         for(var i = 0, len = this.config.length; i < len; i++){
8605             if(this.config[i].dataIndex == dataIndex){
8606                 return i;
8607             }
8608         }
8609         return -1;
8610     },
8611     
8612     
8613     moveColumn : function(oldIndex, newIndex){
8614         var c = this.config[oldIndex];
8615         this.config.splice(oldIndex, 1);
8616         this.config.splice(newIndex, 0, c);
8617         this.dataMap = null;
8618         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8619     },
8620
8621     isLocked : function(colIndex){
8622         return this.config[colIndex].locked === true;
8623     },
8624
8625     setLocked : function(colIndex, value, suppressEvent){
8626         if(this.isLocked(colIndex) == value){
8627             return;
8628         }
8629         this.config[colIndex].locked = value;
8630         if(!suppressEvent){
8631             this.fireEvent("columnlockchange", this, colIndex, value);
8632         }
8633     },
8634
8635     getTotalLockedWidth : function(){
8636         var totalWidth = 0;
8637         for(var i = 0; i < this.config.length; i++){
8638             if(this.isLocked(i) && !this.isHidden(i)){
8639                 this.totalWidth += this.getColumnWidth(i);
8640             }
8641         }
8642         return totalWidth;
8643     },
8644
8645     getLockedCount : function(){
8646         for(var i = 0, len = this.config.length; i < len; i++){
8647             if(!this.isLocked(i)){
8648                 return i;
8649             }
8650         }
8651         
8652         return this.config.length;
8653     },
8654
8655     /**
8656      * Returns the number of columns.
8657      * @return {Number}
8658      */
8659     getColumnCount : function(visibleOnly){
8660         if(visibleOnly === true){
8661             var c = 0;
8662             for(var i = 0, len = this.config.length; i < len; i++){
8663                 if(!this.isHidden(i)){
8664                     c++;
8665                 }
8666             }
8667             return c;
8668         }
8669         return this.config.length;
8670     },
8671
8672     /**
8673      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8674      * @param {Function} fn
8675      * @param {Object} scope (optional)
8676      * @return {Array} result
8677      */
8678     getColumnsBy : function(fn, scope){
8679         var r = [];
8680         for(var i = 0, len = this.config.length; i < len; i++){
8681             var c = this.config[i];
8682             if(fn.call(scope||this, c, i) === true){
8683                 r[r.length] = c;
8684             }
8685         }
8686         return r;
8687     },
8688
8689     /**
8690      * Returns true if the specified column is sortable.
8691      * @param {Number} col The column index
8692      * @return {Boolean}
8693      */
8694     isSortable : function(col){
8695         if(typeof this.config[col].sortable == "undefined"){
8696             return this.defaultSortable;
8697         }
8698         return this.config[col].sortable;
8699     },
8700
8701     /**
8702      * Returns the rendering (formatting) function defined for the column.
8703      * @param {Number} col The column index.
8704      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8705      */
8706     getRenderer : function(col){
8707         if(!this.config[col].renderer){
8708             return Roo.grid.ColumnModel.defaultRenderer;
8709         }
8710         return this.config[col].renderer;
8711     },
8712
8713     /**
8714      * Sets the rendering (formatting) function for a column.
8715      * @param {Number} col The column index
8716      * @param {Function} fn The function to use to process the cell's raw data
8717      * to return HTML markup for the grid view. The render function is called with
8718      * the following parameters:<ul>
8719      * <li>Data value.</li>
8720      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8721      * <li>css A CSS style string to apply to the table cell.</li>
8722      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8723      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8724      * <li>Row index</li>
8725      * <li>Column index</li>
8726      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8727      */
8728     setRenderer : function(col, fn){
8729         this.config[col].renderer = fn;
8730     },
8731
8732     /**
8733      * Returns the width for the specified column.
8734      * @param {Number} col The column index
8735      * @param (optional) {String} gridSize bootstrap width size.
8736      * @return {Number}
8737      */
8738     getColumnWidth : function(col, gridSize)
8739         {
8740                 var cfg = this.config[col];
8741                 
8742                 if (typeof(gridSize) == 'undefined') {
8743                         return cfg.width * 1 || this.defaultWidth;
8744                 }
8745                 if (gridSize === false) { // if we set it..
8746                         return cfg.width || false;
8747                 }
8748                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8749                 
8750                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8751                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8752                                 continue;
8753                         }
8754                         return cfg[ sizes[i] ];
8755                 }
8756                 return 1;
8757                 
8758     },
8759
8760     /**
8761      * Sets the width for a column.
8762      * @param {Number} col The column index
8763      * @param {Number} width The new width
8764      */
8765     setColumnWidth : function(col, width, suppressEvent){
8766         this.config[col].width = width;
8767         this.totalWidth = null;
8768         if(!suppressEvent){
8769              this.fireEvent("widthchange", this, col, width);
8770         }
8771     },
8772
8773     /**
8774      * Returns the total width of all columns.
8775      * @param {Boolean} includeHidden True to include hidden column widths
8776      * @return {Number}
8777      */
8778     getTotalWidth : function(includeHidden){
8779         if(!this.totalWidth){
8780             this.totalWidth = 0;
8781             for(var i = 0, len = this.config.length; i < len; i++){
8782                 if(includeHidden || !this.isHidden(i)){
8783                     this.totalWidth += this.getColumnWidth(i);
8784                 }
8785             }
8786         }
8787         return this.totalWidth;
8788     },
8789
8790     /**
8791      * Returns the header for the specified column.
8792      * @param {Number} col The column index
8793      * @return {String}
8794      */
8795     getColumnHeader : function(col){
8796         return this.config[col].header;
8797     },
8798
8799     /**
8800      * Sets the header for a column.
8801      * @param {Number} col The column index
8802      * @param {String} header The new header
8803      */
8804     setColumnHeader : function(col, header){
8805         this.config[col].header = header;
8806         this.fireEvent("headerchange", this, col, header);
8807     },
8808
8809     /**
8810      * Returns the tooltip for the specified column.
8811      * @param {Number} col The column index
8812      * @return {String}
8813      */
8814     getColumnTooltip : function(col){
8815             return this.config[col].tooltip;
8816     },
8817     /**
8818      * Sets the tooltip for a column.
8819      * @param {Number} col The column index
8820      * @param {String} tooltip The new tooltip
8821      */
8822     setColumnTooltip : function(col, tooltip){
8823             this.config[col].tooltip = tooltip;
8824     },
8825
8826     /**
8827      * Returns the dataIndex for the specified column.
8828      * @param {Number} col The column index
8829      * @return {Number}
8830      */
8831     getDataIndex : function(col){
8832         return this.config[col].dataIndex;
8833     },
8834
8835     /**
8836      * Sets the dataIndex for a column.
8837      * @param {Number} col The column index
8838      * @param {Number} dataIndex The new dataIndex
8839      */
8840     setDataIndex : function(col, dataIndex){
8841         this.config[col].dataIndex = dataIndex;
8842     },
8843
8844     
8845     
8846     /**
8847      * Returns true if the cell is editable.
8848      * @param {Number} colIndex The column index
8849      * @param {Number} rowIndex The row index - this is nto actually used..?
8850      * @return {Boolean}
8851      */
8852     isCellEditable : function(colIndex, rowIndex){
8853         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8854     },
8855
8856     /**
8857      * Returns the editor defined for the cell/column.
8858      * return false or null to disable editing.
8859      * @param {Number} colIndex The column index
8860      * @param {Number} rowIndex The row index
8861      * @return {Object}
8862      */
8863     getCellEditor : function(colIndex, rowIndex){
8864         return this.config[colIndex].editor;
8865     },
8866
8867     /**
8868      * Sets if a column is editable.
8869      * @param {Number} col The column index
8870      * @param {Boolean} editable True if the column is editable
8871      */
8872     setEditable : function(col, editable){
8873         this.config[col].editable = editable;
8874     },
8875
8876
8877     /**
8878      * Returns true if the column is hidden.
8879      * @param {Number} colIndex The column index
8880      * @return {Boolean}
8881      */
8882     isHidden : function(colIndex){
8883         return this.config[colIndex].hidden;
8884     },
8885
8886
8887     /**
8888      * Returns true if the column width cannot be changed
8889      */
8890     isFixed : function(colIndex){
8891         return this.config[colIndex].fixed;
8892     },
8893
8894     /**
8895      * Returns true if the column can be resized
8896      * @return {Boolean}
8897      */
8898     isResizable : function(colIndex){
8899         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8900     },
8901     /**
8902      * Sets if a column is hidden.
8903      * @param {Number} colIndex The column index
8904      * @param {Boolean} hidden True if the column is hidden
8905      */
8906     setHidden : function(colIndex, hidden){
8907         this.config[colIndex].hidden = hidden;
8908         this.totalWidth = null;
8909         this.fireEvent("hiddenchange", this, colIndex, hidden);
8910     },
8911
8912     /**
8913      * Sets the editor for a column.
8914      * @param {Number} col The column index
8915      * @param {Object} editor The editor object
8916      */
8917     setEditor : function(col, editor){
8918         this.config[col].editor = editor;
8919     },
8920     /**
8921      * Add a column (experimental...) - defaults to adding to the end..
8922      * @param {Object} config 
8923     */
8924     addColumn : function(c)
8925     {
8926     
8927         var i = this.config.length;
8928         this.config[i] = c;
8929         
8930         if(typeof c.dataIndex == "undefined"){
8931             c.dataIndex = i;
8932         }
8933         if(typeof c.renderer == "string"){
8934             c.renderer = Roo.util.Format[c.renderer];
8935         }
8936         if(typeof c.id == "undefined"){
8937             c.id = Roo.id();
8938         }
8939         if(c.editor && c.editor.xtype){
8940             c.editor  = Roo.factory(c.editor, Roo.grid);
8941         }
8942         if(c.editor && c.editor.isFormField){
8943             c.editor = new Roo.grid.GridEditor(c.editor);
8944         }
8945         this.lookup[c.id] = c;
8946     }
8947     
8948 });
8949
8950 Roo.grid.ColumnModel.defaultRenderer = function(value)
8951 {
8952     if(typeof value == "object") {
8953         return value;
8954     }
8955         if(typeof value == "string" && value.length < 1){
8956             return "&#160;";
8957         }
8958     
8959         return String.format("{0}", value);
8960 };
8961
8962 // Alias for backwards compatibility
8963 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8964 /*
8965  * Based on:
8966  * Ext JS Library 1.1.1
8967  * Copyright(c) 2006-2007, Ext JS, LLC.
8968  *
8969  * Originally Released Under LGPL - original licence link has changed is not relivant.
8970  *
8971  * Fork - LGPL
8972  * <script type="text/javascript">
8973  */
8974  
8975 /**
8976  * @class Roo.LoadMask
8977  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8978  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8979  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8980  * element's UpdateManager load indicator and will be destroyed after the initial load.
8981  * @constructor
8982  * Create a new LoadMask
8983  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8984  * @param {Object} config The config object
8985  */
8986 Roo.LoadMask = function(el, config){
8987     this.el = Roo.get(el);
8988     Roo.apply(this, config);
8989     if(this.store){
8990         this.store.on('beforeload', this.onBeforeLoad, this);
8991         this.store.on('load', this.onLoad, this);
8992         this.store.on('loadexception', this.onLoadException, this);
8993         this.removeMask = false;
8994     }else{
8995         var um = this.el.getUpdateManager();
8996         um.showLoadIndicator = false; // disable the default indicator
8997         um.on('beforeupdate', this.onBeforeLoad, this);
8998         um.on('update', this.onLoad, this);
8999         um.on('failure', this.onLoad, this);
9000         this.removeMask = true;
9001     }
9002 };
9003
9004 Roo.LoadMask.prototype = {
9005     /**
9006      * @cfg {Boolean} removeMask
9007      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9008      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9009      */
9010     removeMask : false,
9011     /**
9012      * @cfg {String} msg
9013      * The text to display in a centered loading message box (defaults to 'Loading...')
9014      */
9015     msg : 'Loading...',
9016     /**
9017      * @cfg {String} msgCls
9018      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9019      */
9020     msgCls : 'x-mask-loading',
9021
9022     /**
9023      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9024      * @type Boolean
9025      */
9026     disabled: false,
9027
9028     /**
9029      * Disables the mask to prevent it from being displayed
9030      */
9031     disable : function(){
9032        this.disabled = true;
9033     },
9034
9035     /**
9036      * Enables the mask so that it can be displayed
9037      */
9038     enable : function(){
9039         this.disabled = false;
9040     },
9041     
9042     onLoadException : function()
9043     {
9044         Roo.log(arguments);
9045         
9046         if (typeof(arguments[3]) != 'undefined') {
9047             Roo.MessageBox.alert("Error loading",arguments[3]);
9048         } 
9049         /*
9050         try {
9051             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9052                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9053             }   
9054         } catch(e) {
9055             
9056         }
9057         */
9058     
9059         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9060     },
9061     // private
9062     onLoad : function()
9063     {
9064         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9065     },
9066
9067     // private
9068     onBeforeLoad : function(){
9069         if(!this.disabled){
9070             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9071         }
9072     },
9073
9074     // private
9075     destroy : function(){
9076         if(this.store){
9077             this.store.un('beforeload', this.onBeforeLoad, this);
9078             this.store.un('load', this.onLoad, this);
9079             this.store.un('loadexception', this.onLoadException, this);
9080         }else{
9081             var um = this.el.getUpdateManager();
9082             um.un('beforeupdate', this.onBeforeLoad, this);
9083             um.un('update', this.onLoad, this);
9084             um.un('failure', this.onLoad, this);
9085         }
9086     }
9087 };/**
9088  * @class Roo.bootstrap.Table
9089  * @licence LGBL
9090  * @extends Roo.bootstrap.Component
9091  * @children Roo.bootstrap.TableBody
9092  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9093  * Similar to Roo.grid.Grid
9094  * <pre><code>
9095  var table = Roo.factory({
9096     xtype : 'Table',
9097     xns : Roo.bootstrap,
9098     autoSizeColumns: true,
9099     
9100     
9101     store : {
9102         xtype : 'Store',
9103         xns : Roo.data,
9104         remoteSort : true,
9105         sortInfo : { direction : 'ASC', field: 'name' },
9106         proxy : {
9107            xtype : 'HttpProxy',
9108            xns : Roo.data,
9109            method : 'GET',
9110            url : 'https://example.com/some.data.url.json'
9111         },
9112         reader : {
9113            xtype : 'JsonReader',
9114            xns : Roo.data,
9115            fields : [ 'id', 'name', whatever' ],
9116            id : 'id',
9117            root : 'data'
9118         }
9119     },
9120     cm : [
9121         {
9122             xtype : 'ColumnModel',
9123             xns : Roo.grid,
9124             align : 'center',
9125             cursor : 'pointer',
9126             dataIndex : 'is_in_group',
9127             header : "Name",
9128             sortable : true,
9129             renderer : function(v, x , r) {  
9130             
9131                 return String.format("{0}", v)
9132             }
9133             width : 3
9134         } // more columns..
9135     ],
9136     selModel : {
9137         xtype : 'RowSelectionModel',
9138         xns : Roo.bootstrap.Table
9139         // you can add listeners to catch selection change here....
9140     }
9141      
9142
9143  });
9144  // set any options
9145  grid.render(Roo.get("some-div"));
9146 </code></pre>
9147
9148 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9149
9150
9151
9152  *
9153  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9154  * @cfg {Roo.data.Store} store The data store to use
9155  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9156  * 
9157  * @cfg {String} cls table class
9158  *
9159  *
9160  * @cfg {string} empty_results  Text to display for no results 
9161  * @cfg {boolean} striped Should the rows be alternative striped
9162  * @cfg {boolean} bordered Add borders to the table
9163  * @cfg {boolean} hover Add hover highlighting
9164  * @cfg {boolean} condensed Format condensed
9165  * @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,
9166  *                also adds table-responsive (see bootstrap docs for details)
9167  * @cfg {Boolean} loadMask (true|false) default false
9168  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9169  * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9170  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9171  * @cfg {Boolean} rowSelection (true|false) default false
9172  * @cfg {Boolean} cellSelection (true|false) default false
9173  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9174  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9175  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9176  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9177  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9178  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9179  *
9180  * 
9181  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9182  * 
9183  * @constructor
9184  * Create a new Table
9185  * @param {Object} config The config object
9186  */
9187
9188 Roo.bootstrap.Table = function(config)
9189 {
9190     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9191      
9192     // BC...
9193     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9194     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9195     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9196     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9197     
9198     this.view = this; // compat with grid.
9199     
9200     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9201     if (this.sm) {
9202         this.sm.grid = this;
9203         this.selModel = Roo.factory(this.sm, Roo.grid);
9204         this.sm = this.selModel;
9205         this.sm.xmodule = this.xmodule || false;
9206     }
9207     
9208     if (this.cm && typeof(this.cm.config) == 'undefined') {
9209         this.colModel = new Roo.grid.ColumnModel(this.cm);
9210         this.cm = this.colModel;
9211         this.cm.xmodule = this.xmodule || false;
9212     }
9213     if (this.store) {
9214         this.store= Roo.factory(this.store, Roo.data);
9215         this.ds = this.store;
9216         this.ds.xmodule = this.xmodule || false;
9217          
9218     }
9219     if (this.footer && this.store) {
9220         this.footer.dataSource = this.ds;
9221         this.footer = Roo.factory(this.footer);
9222     }
9223     
9224     /** @private */
9225     this.addEvents({
9226         /**
9227          * @event cellclick
9228          * Fires when a cell is clicked
9229          * @param {Roo.bootstrap.Table} this
9230          * @param {Roo.Element} el
9231          * @param {Number} rowIndex
9232          * @param {Number} columnIndex
9233          * @param {Roo.EventObject} e
9234          */
9235         "cellclick" : true,
9236         /**
9237          * @event celldblclick
9238          * Fires when a cell is double clicked
9239          * @param {Roo.bootstrap.Table} this
9240          * @param {Roo.Element} el
9241          * @param {Number} rowIndex
9242          * @param {Number} columnIndex
9243          * @param {Roo.EventObject} e
9244          */
9245         "celldblclick" : true,
9246         /**
9247          * @event rowclick
9248          * Fires when a row is clicked
9249          * @param {Roo.bootstrap.Table} this
9250          * @param {Roo.Element} el
9251          * @param {Number} rowIndex
9252          * @param {Roo.EventObject} e
9253          */
9254         "rowclick" : true,
9255         /**
9256          * @event rowdblclick
9257          * Fires when a row is double clicked
9258          * @param {Roo.bootstrap.Table} this
9259          * @param {Roo.Element} el
9260          * @param {Number} rowIndex
9261          * @param {Roo.EventObject} e
9262          */
9263         "rowdblclick" : true,
9264         /**
9265          * @event mouseover
9266          * Fires when a mouseover occur
9267          * @param {Roo.bootstrap.Table} this
9268          * @param {Roo.Element} el
9269          * @param {Number} rowIndex
9270          * @param {Number} columnIndex
9271          * @param {Roo.EventObject} e
9272          */
9273         "mouseover" : true,
9274         /**
9275          * @event mouseout
9276          * Fires when a mouseout occur
9277          * @param {Roo.bootstrap.Table} this
9278          * @param {Roo.Element} el
9279          * @param {Number} rowIndex
9280          * @param {Number} columnIndex
9281          * @param {Roo.EventObject} e
9282          */
9283         "mouseout" : true,
9284         /**
9285          * @event rowclass
9286          * Fires when a row is rendered, so you can change add a style to it.
9287          * @param {Roo.bootstrap.Table} this
9288          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9289          */
9290         'rowclass' : true,
9291           /**
9292          * @event rowsrendered
9293          * Fires when all the  rows have been rendered
9294          * @param {Roo.bootstrap.Table} this
9295          */
9296         'rowsrendered' : true,
9297         /**
9298          * @event contextmenu
9299          * The raw contextmenu event for the entire grid.
9300          * @param {Roo.EventObject} e
9301          */
9302         "contextmenu" : true,
9303         /**
9304          * @event rowcontextmenu
9305          * Fires when a row is right clicked
9306          * @param {Roo.bootstrap.Table} this
9307          * @param {Number} rowIndex
9308          * @param {Roo.EventObject} e
9309          */
9310         "rowcontextmenu" : true,
9311         /**
9312          * @event cellcontextmenu
9313          * Fires when a cell is right clicked
9314          * @param {Roo.bootstrap.Table} this
9315          * @param {Number} rowIndex
9316          * @param {Number} cellIndex
9317          * @param {Roo.EventObject} e
9318          */
9319          "cellcontextmenu" : true,
9320          /**
9321          * @event headercontextmenu
9322          * Fires when a header is right clicked
9323          * @param {Roo.bootstrap.Table} this
9324          * @param {Number} columnIndex
9325          * @param {Roo.EventObject} e
9326          */
9327         "headercontextmenu" : true,
9328         /**
9329          * @event mousedown
9330          * The raw mousedown event for the entire grid.
9331          * @param {Roo.EventObject} e
9332          */
9333         "mousedown" : true
9334         
9335     });
9336 };
9337
9338 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9339     
9340     cls: false,
9341     
9342     empty_results : '',
9343     striped : false,
9344     scrollBody : false,
9345     bordered: false,
9346     hover:  false,
9347     condensed : false,
9348     responsive : false,
9349     sm : false,
9350     cm : false,
9351     store : false,
9352     loadMask : false,
9353     footerShow : true,
9354     footerRow : false,
9355     headerShow : true,
9356     enableColumnResize: true,
9357     disableAutoSize: false,
9358   
9359     rowSelection : false,
9360     cellSelection : false,
9361     layout : false,
9362
9363     minColumnWidth : 50,
9364     
9365     // Roo.Element - the tbody
9366     bodyEl: false,  // <tbody> Roo.Element - thead element    
9367     headEl: false,  // <thead> Roo.Element - thead element
9368     resizeProxy : false, // proxy element for dragging?
9369
9370
9371     
9372     container: false, // used by gridpanel...
9373     
9374     lazyLoad : false,
9375     
9376     CSS : Roo.util.CSS,
9377     
9378     auto_hide_footer : false,
9379     
9380     view: false, // actually points to this..
9381     
9382     getAutoCreate : function()
9383     {
9384         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9385         
9386         cfg = {
9387             tag: 'table',
9388             cls : 'table', 
9389             cn : []
9390         };
9391         // this get's auto added by panel.Grid
9392         if (this.scrollBody) {
9393             cfg.cls += ' table-body-fixed';
9394         }    
9395         if (this.striped) {
9396             cfg.cls += ' table-striped';
9397         }
9398         
9399         if (this.hover) {
9400             cfg.cls += ' table-hover';
9401         }
9402         if (this.bordered) {
9403             cfg.cls += ' table-bordered';
9404         }
9405         if (this.condensed) {
9406             cfg.cls += ' table-condensed';
9407         }
9408         
9409         if (this.responsive) {
9410             cfg.cls += ' table-responsive';
9411         }
9412         
9413         if (this.cls) {
9414             cfg.cls+=  ' ' +this.cls;
9415         }
9416         
9417         
9418         
9419         if (this.layout) {
9420             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9421         }
9422         
9423         if(this.store || this.cm){
9424             if(this.headerShow){
9425                 cfg.cn.push(this.renderHeader());
9426             }
9427             
9428             cfg.cn.push(this.renderBody());
9429             
9430             if(this.footerShow || this.footerRow){
9431                 cfg.cn.push(this.renderFooter());
9432             }
9433
9434             // where does this come from?
9435             //cfg.cls+=  ' TableGrid';
9436         }
9437         
9438         return { cn : [ cfg ] };
9439     },
9440     
9441     initEvents : function()
9442     {   
9443         if(!this.store || !this.cm){
9444             return;
9445         }
9446         if (this.selModel) {
9447             this.selModel.initEvents();
9448         }
9449         
9450         
9451         //Roo.log('initEvents with ds!!!!');
9452         
9453         this.bodyEl = this.el.select('tbody', true).first();
9454         this.headEl = this.el.select('thead', true).first();
9455         this.mainFoot = this.el.select('tfoot', true).first();
9456         
9457         
9458         
9459         
9460         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9461             e.on('click', this.sort, this);
9462         }, this);
9463         
9464         
9465         // why is this done????? = it breaks dialogs??
9466         //this.parent().el.setStyle('position', 'relative');
9467         
9468         
9469         if (this.footer) {
9470             this.footer.parentId = this.id;
9471             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9472             
9473             if(this.lazyLoad){
9474                 this.el.select('tfoot tr td').first().addClass('hide');
9475             }
9476         } 
9477         
9478         if(this.loadMask) {
9479             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9480         }
9481         
9482         this.store.on('load', this.onLoad, this);
9483         this.store.on('beforeload', this.onBeforeLoad, this);
9484         this.store.on('update', this.onUpdate, this);
9485         this.store.on('add', this.onAdd, this);
9486         this.store.on("clear", this.clear, this);
9487         
9488         this.el.on("contextmenu", this.onContextMenu, this);
9489         
9490         
9491         this.cm.on("headerchange", this.onHeaderChange, this);
9492         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9493
9494  //?? does bodyEl get replaced on render?
9495         this.bodyEl.on("click", this.onClick, this);
9496         this.bodyEl.on("dblclick", this.onDblClick, this);        
9497         this.bodyEl.on('scroll', this.onBodyScroll, this);
9498
9499         // guessing mainbody will work - this relays usually caught by selmodel at present.
9500         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9501   
9502   
9503         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9504         
9505   
9506         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9507             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9508         }
9509         
9510         this.initCSS();
9511     },
9512     // Compatibility with grid - we implement all the view features at present.
9513     getView : function()
9514     {
9515         return this;
9516     },
9517     
9518     initCSS : function()
9519     {
9520         if(this.disableAutoSize) {
9521             return;
9522         }
9523         
9524         var cm = this.cm, styles = [];
9525         this.CSS.removeStyleSheet(this.id + '-cssrules');
9526         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9527         // we can honour xs/sm/md/xl  as widths...
9528         // we first have to decide what widht we are currently at...
9529         var sz = Roo.getGridSize();
9530         
9531         var total = 0;
9532         var last = -1;
9533         var cols = []; // visable cols.
9534         var total_abs = 0;
9535         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9536             var w = cm.getColumnWidth(i, false);
9537             if(cm.isHidden(i)){
9538                 cols.push( { rel : false, abs : 0 });
9539                 continue;
9540             }
9541             if (w !== false) {
9542                 cols.push( { rel : false, abs : w });
9543                 total_abs += w;
9544                 last = i; // not really..
9545                 continue;
9546             }
9547             var w = cm.getColumnWidth(i, sz);
9548             if (w > 0) {
9549                 last = i
9550             }
9551             total += w;
9552             cols.push( { rel : w, abs : false });
9553         }
9554         
9555         var avail = this.bodyEl.dom.clientWidth - total_abs;
9556         
9557         var unitWidth = Math.floor(avail / total);
9558         var rem = avail - (unitWidth * total);
9559         
9560         var hidden, width, pos = 0 , splithide , left;
9561         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9562             
9563             hidden = 'display:none;';
9564             left = '';
9565             width  = 'width:0px;';
9566             splithide = '';
9567             if(!cm.isHidden(i)){
9568                 hidden = '';
9569                 
9570                 
9571                 // we can honour xs/sm/md/xl ?
9572                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9573                 if (w===0) {
9574                     hidden = 'display:none;';
9575                 }
9576                 // width should return a small number...
9577                 if (i == last) {
9578                     w+=rem; // add the remaining with..
9579                 }
9580                 pos += w;
9581                 left = "left:" + (pos -4) + "px;";
9582                 width = "width:" + w+ "px;";
9583                 
9584             }
9585             if (this.responsive) {
9586                 width = '';
9587                 left = '';
9588                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9589                 splithide = 'display: none;';
9590             }
9591             
9592             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9593             if (this.headEl) {
9594                 if (i == last) {
9595                     splithide = 'display:none;';
9596                 }
9597                 
9598                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9599                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9600                             // this is the popover version..
9601                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9602                 );
9603             }
9604             
9605         }
9606         //Roo.log(styles.join(''));
9607         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9608         
9609     },
9610     
9611     
9612     
9613     onContextMenu : function(e, t)
9614     {
9615         this.processEvent("contextmenu", e);
9616     },
9617     
9618     processEvent : function(name, e)
9619     {
9620         if (name != 'touchstart' ) {
9621             this.fireEvent(name, e);    
9622         }
9623         
9624         var t = e.getTarget();
9625         
9626         var cell = Roo.get(t);
9627         
9628         if(!cell){
9629             return;
9630         }
9631         
9632         if(cell.findParent('tfoot', false, true)){
9633             return;
9634         }
9635         
9636         if(cell.findParent('thead', false, true)){
9637             
9638             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9639                 cell = Roo.get(t).findParent('th', false, true);
9640                 if (!cell) {
9641                     Roo.log("failed to find th in thead?");
9642                     Roo.log(e.getTarget());
9643                     return;
9644                 }
9645             }
9646             
9647             var cellIndex = cell.dom.cellIndex;
9648             
9649             var ename = name == 'touchstart' ? 'click' : name;
9650             this.fireEvent("header" + ename, this, cellIndex, e);
9651             
9652             return;
9653         }
9654         
9655         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9656             cell = Roo.get(t).findParent('td', false, true);
9657             if (!cell) {
9658                 Roo.log("failed to find th in tbody?");
9659                 Roo.log(e.getTarget());
9660                 return;
9661             }
9662         }
9663         
9664         var row = cell.findParent('tr', false, true);
9665         var cellIndex = cell.dom.cellIndex;
9666         var rowIndex = row.dom.rowIndex - 1;
9667         
9668         if(row !== false){
9669             
9670             this.fireEvent("row" + name, this, rowIndex, e);
9671             
9672             if(cell !== false){
9673             
9674                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9675             }
9676         }
9677         
9678     },
9679     
9680     onMouseover : function(e, el)
9681     {
9682         var cell = Roo.get(el);
9683         
9684         if(!cell){
9685             return;
9686         }
9687         
9688         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9689             cell = cell.findParent('td', false, true);
9690         }
9691         
9692         var row = cell.findParent('tr', false, true);
9693         var cellIndex = cell.dom.cellIndex;
9694         var rowIndex = row.dom.rowIndex - 1; // start from 0
9695         
9696         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9697         
9698     },
9699     
9700     onMouseout : function(e, el)
9701     {
9702         var cell = Roo.get(el);
9703         
9704         if(!cell){
9705             return;
9706         }
9707         
9708         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9709             cell = cell.findParent('td', false, true);
9710         }
9711         
9712         var row = cell.findParent('tr', false, true);
9713         var cellIndex = cell.dom.cellIndex;
9714         var rowIndex = row.dom.rowIndex - 1; // start from 0
9715         
9716         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9717         
9718     },
9719     
9720     onClick : function(e, el)
9721     {
9722         var cell = Roo.get(el);
9723         
9724         if(!cell || (!this.cellSelection && !this.rowSelection)){
9725             return;
9726         }
9727         
9728         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9729             cell = cell.findParent('td', false, true);
9730         }
9731         
9732         if(!cell || typeof(cell) == 'undefined'){
9733             return;
9734         }
9735         
9736         var row = cell.findParent('tr', false, true);
9737         
9738         if(!row || typeof(row) == 'undefined'){
9739             return;
9740         }
9741         
9742         var cellIndex = cell.dom.cellIndex;
9743         var rowIndex = this.getRowIndex(row);
9744         
9745         // why??? - should these not be based on SelectionModel?
9746         //if(this.cellSelection){
9747             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9748         //}
9749         
9750         //if(this.rowSelection){
9751             this.fireEvent('rowclick', this, row, rowIndex, e);
9752         //}
9753          
9754     },
9755         
9756     onDblClick : function(e,el)
9757     {
9758         var cell = Roo.get(el);
9759         
9760         if(!cell || (!this.cellSelection && !this.rowSelection)){
9761             return;
9762         }
9763         
9764         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9765             cell = cell.findParent('td', false, true);
9766         }
9767         
9768         if(!cell || typeof(cell) == 'undefined'){
9769             return;
9770         }
9771         
9772         var row = cell.findParent('tr', false, true);
9773         
9774         if(!row || typeof(row) == 'undefined'){
9775             return;
9776         }
9777         
9778         var cellIndex = cell.dom.cellIndex;
9779         var rowIndex = this.getRowIndex(row);
9780         
9781         if(this.cellSelection){
9782             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9783         }
9784         
9785         if(this.rowSelection){
9786             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9787         }
9788     },
9789     findRowIndex : function(el)
9790     {
9791         var cell = Roo.get(el);
9792         if(!cell) {
9793             return false;
9794         }
9795         var row = cell.findParent('tr', false, true);
9796         
9797         if(!row || typeof(row) == 'undefined'){
9798             return false;
9799         }
9800         return this.getRowIndex(row);
9801     },
9802     sort : function(e,el)
9803     {
9804         var col = Roo.get(el);
9805         
9806         if(!col.hasClass('sortable')){
9807             return;
9808         }
9809         
9810         var sort = col.attr('sort');
9811         var dir = 'ASC';
9812         
9813         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9814             dir = 'DESC';
9815         }
9816         
9817         this.store.sortInfo = {field : sort, direction : dir};
9818         
9819         if (this.footer) {
9820             Roo.log("calling footer first");
9821             this.footer.onClick('first');
9822         } else {
9823         
9824             this.store.load({ params : { start : 0 } });
9825         }
9826     },
9827     
9828     renderHeader : function()
9829     {
9830         var header = {
9831             tag: 'thead',
9832             cn : []
9833         };
9834         
9835         var cm = this.cm;
9836         this.totalWidth = 0;
9837         
9838         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9839             
9840             var config = cm.config[i];
9841             
9842             var c = {
9843                 tag: 'th',
9844                 cls : 'x-hcol-' + i,
9845                 style : '',
9846                 
9847                 html: cm.getColumnHeader(i)
9848             };
9849             
9850             var tooltip = cm.getColumnTooltip(i);
9851             if (tooltip) {
9852                 c.tooltip = tooltip;
9853             }
9854             
9855             
9856             var hh = '';
9857             
9858             if(typeof(config.sortable) != 'undefined' && config.sortable){
9859                 c.cls += ' sortable';
9860                 c.html = '<i class="fa"></i>' + c.html;
9861             }
9862             
9863             // could use BS4 hidden-..-down 
9864             
9865             if(typeof(config.lgHeader) != 'undefined'){
9866                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9867             }
9868             
9869             if(typeof(config.mdHeader) != 'undefined'){
9870                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9871             }
9872             
9873             if(typeof(config.smHeader) != 'undefined'){
9874                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9875             }
9876             
9877             if(typeof(config.xsHeader) != 'undefined'){
9878                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9879             }
9880             
9881             if(hh.length){
9882                 c.html = hh;
9883             }
9884             
9885             if(typeof(config.tooltip) != 'undefined'){
9886                 c.tooltip = config.tooltip;
9887             }
9888             
9889             if(typeof(config.colspan) != 'undefined'){
9890                 c.colspan = config.colspan;
9891             }
9892             
9893             // hidden is handled by CSS now
9894             
9895             if(typeof(config.dataIndex) != 'undefined'){
9896                 c.sort = config.dataIndex;
9897             }
9898             
9899            
9900             
9901             if(typeof(config.align) != 'undefined' && config.align.length){
9902                 c.style += ' text-align:' + config.align + ';';
9903             }
9904             
9905             /* width is done in CSS
9906              *if(typeof(config.width) != 'undefined'){
9907                 c.style += ' width:' + config.width + 'px;';
9908                 this.totalWidth += config.width;
9909             } else {
9910                 this.totalWidth += 100; // assume minimum of 100 per column?
9911             }
9912             */
9913             
9914             if(typeof(config.cls) != 'undefined'){
9915                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9916             }
9917             // this is the bit that doesnt reall work at all...
9918             
9919             if (this.responsive) {
9920                  
9921             
9922                 ['xs','sm','md','lg'].map(function(size){
9923                     
9924                     if(typeof(config[size]) == 'undefined'){
9925                         return;
9926                     }
9927                      
9928                     if (!config[size]) { // 0 = hidden
9929                         // BS 4 '0' is treated as hide that column and below.
9930                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9931                         return;
9932                     }
9933                     
9934                     c.cls += ' col-' + size + '-' + config[size] + (
9935                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9936                     );
9937                     
9938                     
9939                 });
9940             }
9941             // at the end?
9942             
9943             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9944             
9945             
9946             
9947             
9948             header.cn.push(c)
9949         }
9950         
9951         return header;
9952     },
9953     
9954     renderBody : function()
9955     {
9956         var body = {
9957             tag: 'tbody',
9958             cn : [
9959                 {
9960                     tag: 'tr',
9961                     cn : [
9962                         {
9963                             tag : 'td',
9964                             colspan :  this.cm.getColumnCount()
9965                         }
9966                     ]
9967                 }
9968             ]
9969         };
9970         
9971         return body;
9972     },
9973     
9974     renderFooter : function()
9975     {
9976         var footer = {
9977             tag: 'tfoot',
9978             cn : [
9979                 {
9980                     tag: 'tr',
9981                     cn : [
9982                         {
9983                             tag : 'td',
9984                             colspan :  this.cm.getColumnCount()
9985                         }
9986                     ]
9987                 }
9988             ]
9989         };
9990         
9991         return footer;
9992     },
9993     
9994     onLoad : function()
9995     {
9996 //        Roo.log('ds onload');
9997         this.clear();
9998         
9999         var _this = this;
10000         var cm = this.cm;
10001         var ds = this.store;
10002         
10003         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10004             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10005             if (_this.store.sortInfo) {
10006                     
10007                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10008                     e.select('i', true).addClass(['fa-arrow-up']);
10009                 }
10010                 
10011                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10012                     e.select('i', true).addClass(['fa-arrow-down']);
10013                 }
10014             }
10015         });
10016         
10017         var tbody =  this.bodyEl;
10018               
10019         if(ds.getCount() > 0){
10020             ds.data.each(function(d,rowIndex){
10021                 var row =  this.renderRow(cm, ds, rowIndex);
10022                 
10023                 tbody.createChild(row);
10024                 
10025                 var _this = this;
10026                 
10027                 if(row.cellObjects.length){
10028                     Roo.each(row.cellObjects, function(r){
10029                         _this.renderCellObject(r);
10030                     })
10031                 }
10032                 
10033             }, this);
10034         } else if (this.empty_results.length) {
10035             this.el.mask(this.empty_results, 'no-spinner');
10036         }
10037         
10038         var tfoot = this.el.select('tfoot', true).first();
10039         
10040         if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10041             
10042             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10043             
10044             var total = this.ds.getTotalCount();
10045             
10046             if(this.footer.pageSize < total){
10047                 this.mainFoot.show();
10048             }
10049         }
10050
10051         if(!this.footerShow && this.footerRow) {
10052
10053             var tr = {
10054                 tag : 'tr',
10055                 cn : []
10056             };
10057
10058             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10059                 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10060                 var td = {
10061                     tag: 'td',
10062                     cls : ' x-fcol-' + i,
10063                     html: footer
10064                 };
10065
10066                 tr.cn.push(td);
10067                 
10068             }
10069             
10070             tfoot.dom.innerHTML = '';
10071
10072             tfoot.createChild(tr);
10073         }
10074         
10075         Roo.each(this.el.select('tbody td', true).elements, function(e){
10076             e.on('mouseover', _this.onMouseover, _this);
10077         });
10078         
10079         Roo.each(this.el.select('tbody td', true).elements, function(e){
10080             e.on('mouseout', _this.onMouseout, _this);
10081         });
10082         this.fireEvent('rowsrendered', this);
10083         
10084         this.autoSize();
10085         
10086         this.initCSS(); /// resize cols
10087
10088         
10089     },
10090     
10091     
10092     onUpdate : function(ds,record)
10093     {
10094         this.refreshRow(record);
10095         this.autoSize();
10096     },
10097     
10098     onRemove : function(ds, record, index, isUpdate){
10099         if(isUpdate !== true){
10100             this.fireEvent("beforerowremoved", this, index, record);
10101         }
10102         var bt = this.bodyEl.dom;
10103         
10104         var rows = this.el.select('tbody > tr', true).elements;
10105         
10106         if(typeof(rows[index]) != 'undefined'){
10107             bt.removeChild(rows[index].dom);
10108         }
10109         
10110 //        if(bt.rows[index]){
10111 //            bt.removeChild(bt.rows[index]);
10112 //        }
10113         
10114         if(isUpdate !== true){
10115             //this.stripeRows(index);
10116             //this.syncRowHeights(index, index);
10117             //this.layout();
10118             this.fireEvent("rowremoved", this, index, record);
10119         }
10120     },
10121     
10122     onAdd : function(ds, records, rowIndex)
10123     {
10124         //Roo.log('on Add called');
10125         // - note this does not handle multiple adding very well..
10126         var bt = this.bodyEl.dom;
10127         for (var i =0 ; i < records.length;i++) {
10128             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10129             //Roo.log(records[i]);
10130             //Roo.log(this.store.getAt(rowIndex+i));
10131             this.insertRow(this.store, rowIndex + i, false);
10132             return;
10133         }
10134         
10135     },
10136     
10137     
10138     refreshRow : function(record){
10139         var ds = this.store, index;
10140         if(typeof record == 'number'){
10141             index = record;
10142             record = ds.getAt(index);
10143         }else{
10144             index = ds.indexOf(record);
10145             if (index < 0) {
10146                 return; // should not happen - but seems to 
10147             }
10148         }
10149         this.insertRow(ds, index, true);
10150         this.autoSize();
10151         this.onRemove(ds, record, index+1, true);
10152         this.autoSize();
10153         //this.syncRowHeights(index, index);
10154         //this.layout();
10155         this.fireEvent("rowupdated", this, index, record);
10156     },
10157     // private - called by RowSelection
10158     onRowSelect : function(rowIndex){
10159         var row = this.getRowDom(rowIndex);
10160         row.addClass(['bg-info','info']);
10161     },
10162     // private - called by RowSelection
10163     onRowDeselect : function(rowIndex)
10164     {
10165         if (rowIndex < 0) {
10166             return;
10167         }
10168         var row = this.getRowDom(rowIndex);
10169         row.removeClass(['bg-info','info']);
10170     },
10171       /**
10172      * Focuses the specified row.
10173      * @param {Number} row The row index
10174      */
10175     focusRow : function(row)
10176     {
10177         //Roo.log('GridView.focusRow');
10178         var x = this.bodyEl.dom.scrollLeft;
10179         this.focusCell(row, 0, false);
10180         this.bodyEl.dom.scrollLeft = x;
10181
10182     },
10183      /**
10184      * Focuses the specified cell.
10185      * @param {Number} row The row index
10186      * @param {Number} col The column index
10187      * @param {Boolean} hscroll false to disable horizontal scrolling
10188      */
10189     focusCell : function(row, col, hscroll)
10190     {
10191         //Roo.log('GridView.focusCell');
10192         var el = this.ensureVisible(row, col, hscroll);
10193         // not sure what focusEL achives = it's a <a> pos relative 
10194         //this.focusEl.alignTo(el, "tl-tl");
10195         //if(Roo.isGecko){
10196         //    this.focusEl.focus();
10197         //}else{
10198         //    this.focusEl.focus.defer(1, this.focusEl);
10199         //}
10200     },
10201     
10202      /**
10203      * Scrolls the specified cell into view
10204      * @param {Number} row The row index
10205      * @param {Number} col The column index
10206      * @param {Boolean} hscroll false to disable horizontal scrolling
10207      */
10208     ensureVisible : function(row, col, hscroll)
10209     {
10210         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10211         //return null; //disable for testing.
10212         if(typeof row != "number"){
10213             row = row.rowIndex;
10214         }
10215         if(row < 0 && row >= this.ds.getCount()){
10216             return  null;
10217         }
10218         col = (col !== undefined ? col : 0);
10219         var cm = this.cm;
10220         while(cm.isHidden(col)){
10221             col++;
10222         }
10223
10224         var el = this.getCellDom(row, col);
10225         if(!el){
10226             return null;
10227         }
10228         var c = this.bodyEl.dom;
10229
10230         var ctop = parseInt(el.offsetTop, 10);
10231         var cleft = parseInt(el.offsetLeft, 10);
10232         var cbot = ctop + el.offsetHeight;
10233         var cright = cleft + el.offsetWidth;
10234
10235         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10236         var ch = 0; //?? header is not withing the area?
10237         var stop = parseInt(c.scrollTop, 10);
10238         var sleft = parseInt(c.scrollLeft, 10);
10239         var sbot = stop + ch;
10240         var sright = sleft + c.clientWidth;
10241         /*
10242         Roo.log('GridView.ensureVisible:' +
10243                 ' ctop:' + ctop +
10244                 ' c.clientHeight:' + c.clientHeight +
10245                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10246                 ' stop:' + stop +
10247                 ' cbot:' + cbot +
10248                 ' sbot:' + sbot +
10249                 ' ch:' + ch  
10250                 );
10251         */
10252         if(ctop < stop){
10253             c.scrollTop = ctop;
10254             //Roo.log("set scrolltop to ctop DISABLE?");
10255         }else if(cbot > sbot){
10256             //Roo.log("set scrolltop to cbot-ch");
10257             c.scrollTop = cbot-ch;
10258         }
10259
10260         if(hscroll !== false){
10261             if(cleft < sleft){
10262                 c.scrollLeft = cleft;
10263             }else if(cright > sright){
10264                 c.scrollLeft = cright-c.clientWidth;
10265             }
10266         }
10267
10268         return el;
10269     },
10270     
10271     
10272     insertRow : function(dm, rowIndex, isUpdate){
10273         
10274         if(!isUpdate){
10275             this.fireEvent("beforerowsinserted", this, rowIndex);
10276         }
10277             //var s = this.getScrollState();
10278         var row = this.renderRow(this.cm, this.store, rowIndex);
10279         // insert before rowIndex..
10280         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10281         
10282         var _this = this;
10283                 
10284         if(row.cellObjects.length){
10285             Roo.each(row.cellObjects, function(r){
10286                 _this.renderCellObject(r);
10287             })
10288         }
10289             
10290         if(!isUpdate){
10291             this.fireEvent("rowsinserted", this, rowIndex);
10292             //this.syncRowHeights(firstRow, lastRow);
10293             //this.stripeRows(firstRow);
10294             //this.layout();
10295         }
10296         
10297     },
10298     
10299     
10300     getRowDom : function(rowIndex)
10301     {
10302         var rows = this.el.select('tbody > tr', true).elements;
10303         
10304         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10305         
10306     },
10307     getCellDom : function(rowIndex, colIndex)
10308     {
10309         var row = this.getRowDom(rowIndex);
10310         if (row === false) {
10311             return false;
10312         }
10313         var cols = row.select('td', true).elements;
10314         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10315         
10316     },
10317     
10318     // returns the object tree for a tr..
10319   
10320     
10321     renderRow : function(cm, ds, rowIndex) 
10322     {
10323         var d = ds.getAt(rowIndex);
10324         
10325         var row = {
10326             tag : 'tr',
10327             cls : 'x-row-' + rowIndex,
10328             cn : []
10329         };
10330             
10331         var cellObjects = [];
10332         
10333         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10334             var config = cm.config[i];
10335             
10336             var renderer = cm.getRenderer(i);
10337             var value = '';
10338             var id = false;
10339             
10340             if(typeof(renderer) !== 'undefined'){
10341                 value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d);
10342             }
10343             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10344             // and are rendered into the cells after the row is rendered - using the id for the element.
10345             
10346             if(typeof(value) === 'object'){
10347                 id = Roo.id();
10348                 cellObjects.push({
10349                     container : id,
10350                     cfg : value 
10351                 })
10352             }
10353             
10354             var rowcfg = {
10355                 record: d,
10356                 rowIndex : rowIndex,
10357                 colIndex : i,
10358                 rowClass : ''
10359             };
10360
10361             this.fireEvent('rowclass', this, rowcfg);
10362             
10363             var td = {
10364                 tag: 'td',
10365                 // this might end up displaying HTML?
10366                 // this is too messy... - better to only do it on columsn you know are going to be too long
10367                 //tooltip : (typeof(value) === 'object') ? '' : value,
10368                 cls : rowcfg.rowClass + ' x-col-' + i,
10369                 style: '',
10370                 html: (typeof(value) === 'object') ? '' : value
10371             };
10372             
10373             if (id) {
10374                 td.id = id;
10375             }
10376             
10377             if(typeof(config.colspan) != 'undefined'){
10378                 td.colspan = config.colspan;
10379             }
10380             
10381             
10382             
10383             if(typeof(config.align) != 'undefined' && config.align.length){
10384                 td.style += ' text-align:' + config.align + ';';
10385             }
10386             if(typeof(config.valign) != 'undefined' && config.valign.length){
10387                 td.style += ' vertical-align:' + config.valign + ';';
10388             }
10389             /*
10390             if(typeof(config.width) != 'undefined'){
10391                 td.style += ' width:' +  config.width + 'px;';
10392             }
10393             */
10394             
10395             if(typeof(config.cursor) != 'undefined'){
10396                 td.style += ' cursor:' +  config.cursor + ';';
10397             }
10398             
10399             if(typeof(config.cls) != 'undefined'){
10400                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10401             }
10402             if (this.responsive) {
10403                 ['xs','sm','md','lg'].map(function(size){
10404                     
10405                     if(typeof(config[size]) == 'undefined'){
10406                         return;
10407                     }
10408                     
10409                     
10410                       
10411                     if (!config[size]) { // 0 = hidden
10412                         // BS 4 '0' is treated as hide that column and below.
10413                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10414                         return;
10415                     }
10416                     
10417                     td.cls += ' col-' + size + '-' + config[size] + (
10418                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10419                     );
10420                      
10421     
10422                 });
10423             }
10424             row.cn.push(td);
10425            
10426         }
10427         
10428         row.cellObjects = cellObjects;
10429         
10430         return row;
10431           
10432     },
10433     
10434     
10435     
10436     onBeforeLoad : function()
10437     {
10438         this.el.unmask(); // if needed.
10439     },
10440      /**
10441      * Remove all rows
10442      */
10443     clear : function()
10444     {
10445         this.el.select('tbody', true).first().dom.innerHTML = '';
10446     },
10447     /**
10448      * Show or hide a row.
10449      * @param {Number} rowIndex to show or hide
10450      * @param {Boolean} state hide
10451      */
10452     setRowVisibility : function(rowIndex, state)
10453     {
10454         var bt = this.bodyEl.dom;
10455         
10456         var rows = this.el.select('tbody > tr', true).elements;
10457         
10458         if(typeof(rows[rowIndex]) == 'undefined'){
10459             return;
10460         }
10461         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10462         
10463     },
10464     
10465     
10466     getSelectionModel : function(){
10467         if(!this.selModel){
10468             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10469         }
10470         return this.selModel;
10471     },
10472     /*
10473      * Render the Roo.bootstrap object from renderder
10474      */
10475     renderCellObject : function(r)
10476     {
10477         var _this = this;
10478         
10479         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10480         
10481         var t = r.cfg.render(r.container);
10482         
10483         if(r.cfg.cn){
10484             Roo.each(r.cfg.cn, function(c){
10485                 var child = {
10486                     container: t.getChildContainer(),
10487                     cfg: c
10488                 };
10489                 _this.renderCellObject(child);
10490             })
10491         }
10492     },
10493     /**
10494      * get the Row Index from a dom element.
10495      * @param {Roo.Element} row The row to look for
10496      * @returns {Number} the row
10497      */
10498     getRowIndex : function(row)
10499     {
10500         var rowIndex = -1;
10501         
10502         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10503             if(el != row){
10504                 return;
10505             }
10506             
10507             rowIndex = index;
10508         });
10509         
10510         return rowIndex;
10511     },
10512     /**
10513      * get the header TH element for columnIndex
10514      * @param {Number} columnIndex
10515      * @returns {Roo.Element}
10516      */
10517     getHeaderIndex: function(colIndex)
10518     {
10519         var cols = this.headEl.select('th', true).elements;
10520         return cols[colIndex]; 
10521     },
10522     /**
10523      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10524      * @param {domElement} cell to look for
10525      * @returns {Number} the column
10526      */
10527     getCellIndex : function(cell)
10528     {
10529         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10530         if(id){
10531             return parseInt(id[1], 10);
10532         }
10533         return 0;
10534     },
10535      /**
10536      * Returns the grid's underlying element = used by panel.Grid
10537      * @return {Element} The element
10538      */
10539     getGridEl : function(){
10540         return this.el;
10541     },
10542      /**
10543      * Forces a resize - used by panel.Grid
10544      * @return {Element} The element
10545      */
10546     autoSize : function()
10547     {
10548         if(this.disableAutoSize) {
10549             return;
10550         }
10551         //var ctr = Roo.get(this.container.dom.parentElement);
10552         var ctr = Roo.get(this.el.dom);
10553         
10554         var thd = this.getGridEl().select('thead',true).first();
10555         var tbd = this.getGridEl().select('tbody', true).first();
10556         var tfd = this.getGridEl().select('tfoot', true).first();
10557         
10558         var cw = ctr.getWidth();
10559         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10560         
10561         if (tbd) {
10562             
10563             tbd.setWidth(ctr.getWidth());
10564             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10565             // this needs fixing for various usage - currently only hydra job advers I think..
10566             //tdb.setHeight(
10567             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10568             //); 
10569             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10570             cw -= barsize;
10571         }
10572         cw = Math.max(cw, this.totalWidth);
10573         this.getGridEl().select('tbody tr',true).setWidth(cw);
10574         this.initCSS();
10575         
10576         // resize 'expandable coloumn?
10577         
10578         return; // we doe not have a view in this design..
10579         
10580     },
10581     onBodyScroll: function()
10582     {
10583         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10584         if(this.headEl){
10585             this.headEl.setStyle({
10586                 'position' : 'relative',
10587                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10588             });
10589         }
10590         
10591         if(this.lazyLoad){
10592             
10593             var scrollHeight = this.bodyEl.dom.scrollHeight;
10594             
10595             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10596             
10597             var height = this.bodyEl.getHeight();
10598             
10599             if(scrollHeight - height == scrollTop) {
10600                 
10601                 var total = this.ds.getTotalCount();
10602                 
10603                 if(this.footer.cursor + this.footer.pageSize < total){
10604                     
10605                     this.footer.ds.load({
10606                         params : {
10607                             start : this.footer.cursor + this.footer.pageSize,
10608                             limit : this.footer.pageSize
10609                         },
10610                         add : true
10611                     });
10612                 }
10613             }
10614             
10615         }
10616     },
10617     onColumnSplitterMoved : function(i, diff)
10618     {
10619         this.userResized = true;
10620         
10621         var cm = this.colModel;
10622         
10623         var w = this.getHeaderIndex(i).getWidth() + diff;
10624         
10625         
10626         cm.setColumnWidth(i, w, true);
10627         this.initCSS();
10628         //var cid = cm.getColumnId(i); << not used in this version?
10629        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10630         
10631         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10632         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10633         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10634 */
10635         //this.updateSplitters();
10636         //this.layout(); << ??
10637         this.fireEvent("columnresize", i, w);
10638     },
10639     onHeaderChange : function()
10640     {
10641         var header = this.renderHeader();
10642         var table = this.el.select('table', true).first();
10643         
10644         this.headEl.remove();
10645         this.headEl = table.createChild(header, this.bodyEl, false);
10646         
10647         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10648             e.on('click', this.sort, this);
10649         }, this);
10650         
10651         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10652             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10653         }
10654         
10655     },
10656     
10657     onHiddenChange : function(colModel, colIndex, hidden)
10658     {
10659         /*
10660         this.cm.setHidden()
10661         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10662         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10663         
10664         this.CSS.updateRule(thSelector, "display", "");
10665         this.CSS.updateRule(tdSelector, "display", "");
10666         
10667         if(hidden){
10668             this.CSS.updateRule(thSelector, "display", "none");
10669             this.CSS.updateRule(tdSelector, "display", "none");
10670         }
10671         */
10672         // onload calls initCSS()
10673         this.onHeaderChange();
10674         this.onLoad();
10675     },
10676     
10677     setColumnWidth: function(col_index, width)
10678     {
10679         // width = "md-2 xs-2..."
10680         if(!this.colModel.config[col_index]) {
10681             return;
10682         }
10683         
10684         var w = width.split(" ");
10685         
10686         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10687         
10688         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10689         
10690         
10691         for(var j = 0; j < w.length; j++) {
10692             
10693             if(!w[j]) {
10694                 continue;
10695             }
10696             
10697             var size_cls = w[j].split("-");
10698             
10699             if(!Number.isInteger(size_cls[1] * 1)) {
10700                 continue;
10701             }
10702             
10703             if(!this.colModel.config[col_index][size_cls[0]]) {
10704                 continue;
10705             }
10706             
10707             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10708                 continue;
10709             }
10710             
10711             h_row[0].classList.replace(
10712                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10713                 "col-"+size_cls[0]+"-"+size_cls[1]
10714             );
10715             
10716             for(var i = 0; i < rows.length; i++) {
10717                 
10718                 var size_cls = w[j].split("-");
10719                 
10720                 if(!Number.isInteger(size_cls[1] * 1)) {
10721                     continue;
10722                 }
10723                 
10724                 if(!this.colModel.config[col_index][size_cls[0]]) {
10725                     continue;
10726                 }
10727                 
10728                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10729                     continue;
10730                 }
10731                 
10732                 rows[i].classList.replace(
10733                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10734                     "col-"+size_cls[0]+"-"+size_cls[1]
10735                 );
10736             }
10737             
10738             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10739         }
10740     }
10741 });
10742
10743 // currently only used to find the split on drag.. 
10744 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10745
10746 /**
10747  * @depricated
10748 */
10749 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10750 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10751 /*
10752  * - LGPL
10753  *
10754  * table cell
10755  * 
10756  */
10757
10758 /**
10759  * @class Roo.bootstrap.TableCell
10760  * @extends Roo.bootstrap.Component
10761  * @children Roo.bootstrap.Component
10762  * @parent Roo.bootstrap.TableRow
10763  * Bootstrap TableCell class
10764  * 
10765  * @cfg {String} html cell contain text
10766  * @cfg {String} cls cell class
10767  * @cfg {String} tag cell tag (td|th) default td
10768  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10769  * @cfg {String} align Aligns the content in a cell
10770  * @cfg {String} axis Categorizes cells
10771  * @cfg {String} bgcolor Specifies the background color of a cell
10772  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10773  * @cfg {Number} colspan Specifies the number of columns a cell should span
10774  * @cfg {String} headers Specifies one or more header cells a cell is related to
10775  * @cfg {Number} height Sets the height of a cell
10776  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10777  * @cfg {Number} rowspan Sets the number of rows a cell should span
10778  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10779  * @cfg {String} valign Vertical aligns the content in a cell
10780  * @cfg {Number} width Specifies the width of a cell
10781  * 
10782  * @constructor
10783  * Create a new TableCell
10784  * @param {Object} config The config object
10785  */
10786
10787 Roo.bootstrap.TableCell = function(config){
10788     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10789 };
10790
10791 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10792     
10793     html: false,
10794     cls: false,
10795     tag: false,
10796     abbr: false,
10797     align: false,
10798     axis: false,
10799     bgcolor: false,
10800     charoff: false,
10801     colspan: false,
10802     headers: false,
10803     height: false,
10804     nowrap: false,
10805     rowspan: false,
10806     scope: false,
10807     valign: false,
10808     width: false,
10809     
10810     
10811     getAutoCreate : function(){
10812         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10813         
10814         cfg = {
10815             tag: 'td'
10816         };
10817         
10818         if(this.tag){
10819             cfg.tag = this.tag;
10820         }
10821         
10822         if (this.html) {
10823             cfg.html=this.html
10824         }
10825         if (this.cls) {
10826             cfg.cls=this.cls
10827         }
10828         if (this.abbr) {
10829             cfg.abbr=this.abbr
10830         }
10831         if (this.align) {
10832             cfg.align=this.align
10833         }
10834         if (this.axis) {
10835             cfg.axis=this.axis
10836         }
10837         if (this.bgcolor) {
10838             cfg.bgcolor=this.bgcolor
10839         }
10840         if (this.charoff) {
10841             cfg.charoff=this.charoff
10842         }
10843         if (this.colspan) {
10844             cfg.colspan=this.colspan
10845         }
10846         if (this.headers) {
10847             cfg.headers=this.headers
10848         }
10849         if (this.height) {
10850             cfg.height=this.height
10851         }
10852         if (this.nowrap) {
10853             cfg.nowrap=this.nowrap
10854         }
10855         if (this.rowspan) {
10856             cfg.rowspan=this.rowspan
10857         }
10858         if (this.scope) {
10859             cfg.scope=this.scope
10860         }
10861         if (this.valign) {
10862             cfg.valign=this.valign
10863         }
10864         if (this.width) {
10865             cfg.width=this.width
10866         }
10867         
10868         
10869         return cfg;
10870     }
10871    
10872 });
10873
10874  
10875
10876  /*
10877  * - LGPL
10878  *
10879  * table row
10880  * 
10881  */
10882
10883 /**
10884  * @class Roo.bootstrap.TableRow
10885  * @extends Roo.bootstrap.Component
10886  * @children Roo.bootstrap.TableCell
10887  * @parent Roo.bootstrap.TableBody
10888  * Bootstrap TableRow class
10889  * @cfg {String} cls row class
10890  * @cfg {String} align Aligns the content in a table row
10891  * @cfg {String} bgcolor Specifies a background color for a table row
10892  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10893  * @cfg {String} valign Vertical aligns the content in a table row
10894  * 
10895  * @constructor
10896  * Create a new TableRow
10897  * @param {Object} config The config object
10898  */
10899
10900 Roo.bootstrap.TableRow = function(config){
10901     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10902 };
10903
10904 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10905     
10906     cls: false,
10907     align: false,
10908     bgcolor: false,
10909     charoff: false,
10910     valign: false,
10911     
10912     getAutoCreate : function(){
10913         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10914         
10915         cfg = {
10916             tag: 'tr'
10917         };
10918             
10919         if(this.cls){
10920             cfg.cls = this.cls;
10921         }
10922         if(this.align){
10923             cfg.align = this.align;
10924         }
10925         if(this.bgcolor){
10926             cfg.bgcolor = this.bgcolor;
10927         }
10928         if(this.charoff){
10929             cfg.charoff = this.charoff;
10930         }
10931         if(this.valign){
10932             cfg.valign = this.valign;
10933         }
10934         
10935         return cfg;
10936     }
10937    
10938 });
10939
10940  
10941
10942  /*
10943  * - LGPL
10944  *
10945  * table body
10946  * 
10947  */
10948
10949 /**
10950  * @class Roo.bootstrap.TableBody
10951  * @extends Roo.bootstrap.Component
10952  * @children Roo.bootstrap.TableRow
10953  * @parent Roo.bootstrap.Table
10954  * Bootstrap TableBody class
10955  * @cfg {String} cls element class
10956  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10957  * @cfg {String} align Aligns the content inside the element
10958  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10959  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10960  * 
10961  * @constructor
10962  * Create a new TableBody
10963  * @param {Object} config The config object
10964  */
10965
10966 Roo.bootstrap.TableBody = function(config){
10967     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10968 };
10969
10970 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10971     
10972     cls: false,
10973     tag: false,
10974     align: false,
10975     charoff: false,
10976     valign: false,
10977     
10978     getAutoCreate : function(){
10979         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10980         
10981         cfg = {
10982             tag: 'tbody'
10983         };
10984             
10985         if (this.cls) {
10986             cfg.cls=this.cls
10987         }
10988         if(this.tag){
10989             cfg.tag = this.tag;
10990         }
10991         
10992         if(this.align){
10993             cfg.align = this.align;
10994         }
10995         if(this.charoff){
10996             cfg.charoff = this.charoff;
10997         }
10998         if(this.valign){
10999             cfg.valign = this.valign;
11000         }
11001         
11002         return cfg;
11003     }
11004     
11005     
11006 //    initEvents : function()
11007 //    {
11008 //        
11009 //        if(!this.store){
11010 //            return;
11011 //        }
11012 //        
11013 //        this.store = Roo.factory(this.store, Roo.data);
11014 //        this.store.on('load', this.onLoad, this);
11015 //        
11016 //        this.store.load();
11017 //        
11018 //    },
11019 //    
11020 //    onLoad: function () 
11021 //    {   
11022 //        this.fireEvent('load', this);
11023 //    }
11024 //    
11025 //   
11026 });
11027
11028  
11029
11030  /*
11031  * Based on:
11032  * Ext JS Library 1.1.1
11033  * Copyright(c) 2006-2007, Ext JS, LLC.
11034  *
11035  * Originally Released Under LGPL - original licence link has changed is not relivant.
11036  *
11037  * Fork - LGPL
11038  * <script type="text/javascript">
11039  */
11040
11041 // as we use this in bootstrap.
11042 Roo.namespace('Roo.form');
11043  /**
11044  * @class Roo.form.Action
11045  * Internal Class used to handle form actions
11046  * @constructor
11047  * @param {Roo.form.BasicForm} el The form element or its id
11048  * @param {Object} config Configuration options
11049  */
11050
11051  
11052  
11053 // define the action interface
11054 Roo.form.Action = function(form, options){
11055     this.form = form;
11056     this.options = options || {};
11057 };
11058 /**
11059  * Client Validation Failed
11060  * @const 
11061  */
11062 Roo.form.Action.CLIENT_INVALID = 'client';
11063 /**
11064  * Server Validation Failed
11065  * @const 
11066  */
11067 Roo.form.Action.SERVER_INVALID = 'server';
11068  /**
11069  * Connect to Server Failed
11070  * @const 
11071  */
11072 Roo.form.Action.CONNECT_FAILURE = 'connect';
11073 /**
11074  * Reading Data from Server Failed
11075  * @const 
11076  */
11077 Roo.form.Action.LOAD_FAILURE = 'load';
11078
11079 Roo.form.Action.prototype = {
11080     type : 'default',
11081     failureType : undefined,
11082     response : undefined,
11083     result : undefined,
11084
11085     // interface method
11086     run : function(options){
11087
11088     },
11089
11090     // interface method
11091     success : function(response){
11092
11093     },
11094
11095     // interface method
11096     handleResponse : function(response){
11097
11098     },
11099
11100     // default connection failure
11101     failure : function(response){
11102         
11103         this.response = response;
11104         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11105         this.form.afterAction(this, false);
11106     },
11107
11108     processResponse : function(response){
11109         this.response = response;
11110         if(!response.responseText){
11111             return true;
11112         }
11113         this.result = this.handleResponse(response);
11114         return this.result;
11115     },
11116
11117     // utility functions used internally
11118     getUrl : function(appendParams){
11119         var url = this.options.url || this.form.url || this.form.el.dom.action;
11120         if(appendParams){
11121             var p = this.getParams();
11122             if(p){
11123                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11124             }
11125         }
11126         return url;
11127     },
11128
11129     getMethod : function(){
11130         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11131     },
11132
11133     getParams : function(){
11134         var bp = this.form.baseParams;
11135         var p = this.options.params;
11136         if(p){
11137             if(typeof p == "object"){
11138                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11139             }else if(typeof p == 'string' && bp){
11140                 p += '&' + Roo.urlEncode(bp);
11141             }
11142         }else if(bp){
11143             p = Roo.urlEncode(bp);
11144         }
11145         return p;
11146     },
11147
11148     createCallback : function(){
11149         return {
11150             success: this.success,
11151             failure: this.failure,
11152             scope: this,
11153             timeout: (this.form.timeout*1000),
11154             upload: this.form.fileUpload ? this.success : undefined
11155         };
11156     }
11157 };
11158
11159 Roo.form.Action.Submit = function(form, options){
11160     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11161 };
11162
11163 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11164     type : 'submit',
11165
11166     haveProgress : false,
11167     uploadComplete : false,
11168     
11169     // uploadProgress indicator.
11170     uploadProgress : function()
11171     {
11172         if (!this.form.progressUrl) {
11173             return;
11174         }
11175         
11176         if (!this.haveProgress) {
11177             Roo.MessageBox.progress("Uploading", "Uploading");
11178         }
11179         if (this.uploadComplete) {
11180            Roo.MessageBox.hide();
11181            return;
11182         }
11183         
11184         this.haveProgress = true;
11185    
11186         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11187         
11188         var c = new Roo.data.Connection();
11189         c.request({
11190             url : this.form.progressUrl,
11191             params: {
11192                 id : uid
11193             },
11194             method: 'GET',
11195             success : function(req){
11196                //console.log(data);
11197                 var rdata = false;
11198                 var edata;
11199                 try  {
11200                    rdata = Roo.decode(req.responseText)
11201                 } catch (e) {
11202                     Roo.log("Invalid data from server..");
11203                     Roo.log(edata);
11204                     return;
11205                 }
11206                 if (!rdata || !rdata.success) {
11207                     Roo.log(rdata);
11208                     Roo.MessageBox.alert(Roo.encode(rdata));
11209                     return;
11210                 }
11211                 var data = rdata.data;
11212                 
11213                 if (this.uploadComplete) {
11214                    Roo.MessageBox.hide();
11215                    return;
11216                 }
11217                    
11218                 if (data){
11219                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11220                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11221                     );
11222                 }
11223                 this.uploadProgress.defer(2000,this);
11224             },
11225        
11226             failure: function(data) {
11227                 Roo.log('progress url failed ');
11228                 Roo.log(data);
11229             },
11230             scope : this
11231         });
11232            
11233     },
11234     
11235     
11236     run : function()
11237     {
11238         // run get Values on the form, so it syncs any secondary forms.
11239         this.form.getValues();
11240         
11241         var o = this.options;
11242         var method = this.getMethod();
11243         var isPost = method == 'POST';
11244         if(o.clientValidation === false || this.form.isValid()){
11245             
11246             if (this.form.progressUrl) {
11247                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11248                     (new Date() * 1) + '' + Math.random());
11249                     
11250             } 
11251             
11252             
11253             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11254                 form:this.form.el.dom,
11255                 url:this.getUrl(!isPost),
11256                 method: method,
11257                 params:isPost ? this.getParams() : null,
11258                 isUpload: this.form.fileUpload,
11259                 formData : this.form.formData
11260             }));
11261             
11262             this.uploadProgress();
11263
11264         }else if (o.clientValidation !== false){ // client validation failed
11265             this.failureType = Roo.form.Action.CLIENT_INVALID;
11266             this.form.afterAction(this, false);
11267         }
11268     },
11269
11270     success : function(response)
11271     {
11272         this.uploadComplete= true;
11273         if (this.haveProgress) {
11274             Roo.MessageBox.hide();
11275         }
11276         
11277         
11278         var result = this.processResponse(response);
11279         if(result === true || result.success){
11280             this.form.afterAction(this, true);
11281             return;
11282         }
11283         if(result.errors){
11284             this.form.markInvalid(result.errors);
11285             this.failureType = Roo.form.Action.SERVER_INVALID;
11286         }
11287         this.form.afterAction(this, false);
11288     },
11289     failure : function(response)
11290     {
11291         this.uploadComplete= true;
11292         if (this.haveProgress) {
11293             Roo.MessageBox.hide();
11294         }
11295         
11296         this.response = response;
11297         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11298         this.form.afterAction(this, false);
11299     },
11300     
11301     handleResponse : function(response){
11302         if(this.form.errorReader){
11303             var rs = this.form.errorReader.read(response);
11304             var errors = [];
11305             if(rs.records){
11306                 for(var i = 0, len = rs.records.length; i < len; i++) {
11307                     var r = rs.records[i];
11308                     errors[i] = r.data;
11309                 }
11310             }
11311             if(errors.length < 1){
11312                 errors = null;
11313             }
11314             return {
11315                 success : rs.success,
11316                 errors : errors
11317             };
11318         }
11319         var ret = false;
11320         try {
11321             var rt = response.responseText;
11322             if (rt.match(/^\<!--\[CDATA\[/)) {
11323                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11324                 rt = rt.replace(/\]\]--\>$/,'');
11325             }
11326             
11327             ret = Roo.decode(rt);
11328         } catch (e) {
11329             ret = {
11330                 success: false,
11331                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11332                 errors : []
11333             };
11334         }
11335         return ret;
11336         
11337     }
11338 });
11339
11340
11341 Roo.form.Action.Load = function(form, options){
11342     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11343     this.reader = this.form.reader;
11344 };
11345
11346 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11347     type : 'load',
11348
11349     run : function(){
11350         
11351         Roo.Ajax.request(Roo.apply(
11352                 this.createCallback(), {
11353                     method:this.getMethod(),
11354                     url:this.getUrl(false),
11355                     params:this.getParams()
11356         }));
11357     },
11358
11359     success : function(response){
11360         
11361         var result = this.processResponse(response);
11362         if(result === true || !result.success || !result.data){
11363             this.failureType = Roo.form.Action.LOAD_FAILURE;
11364             this.form.afterAction(this, false);
11365             return;
11366         }
11367         this.form.clearInvalid();
11368         this.form.setValues(result.data);
11369         this.form.afterAction(this, true);
11370     },
11371
11372     handleResponse : function(response){
11373         if(this.form.reader){
11374             var rs = this.form.reader.read(response);
11375             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11376             return {
11377                 success : rs.success,
11378                 data : data
11379             };
11380         }
11381         return Roo.decode(response.responseText);
11382     }
11383 });
11384
11385 Roo.form.Action.ACTION_TYPES = {
11386     'load' : Roo.form.Action.Load,
11387     'submit' : Roo.form.Action.Submit
11388 };/*
11389  * - LGPL
11390  *
11391  * form
11392  *
11393  */
11394
11395 /**
11396  * @class Roo.bootstrap.form.Form
11397  * @extends Roo.bootstrap.Component
11398  * @children Roo.bootstrap.Component
11399  * Bootstrap Form class
11400  * @cfg {String} method  GET | POST (default POST)
11401  * @cfg {String} labelAlign top | left (default top)
11402  * @cfg {String} align left  | right - for navbars
11403  * @cfg {Boolean} loadMask load mask when submit (default true)
11404
11405  *
11406  * @constructor
11407  * Create a new Form
11408  * @param {Object} config The config object
11409  */
11410
11411
11412 Roo.bootstrap.form.Form = function(config){
11413     
11414     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11415     
11416     Roo.bootstrap.form.Form.popover.apply();
11417     
11418     this.addEvents({
11419         /**
11420          * @event clientvalidation
11421          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11422          * @param {Form} this
11423          * @param {Boolean} valid true if the form has passed client-side validation
11424          */
11425         clientvalidation: true,
11426         /**
11427          * @event beforeaction
11428          * Fires before any action is performed. Return false to cancel the action.
11429          * @param {Form} this
11430          * @param {Action} action The action to be performed
11431          */
11432         beforeaction: true,
11433         /**
11434          * @event actionfailed
11435          * Fires when an action fails.
11436          * @param {Form} this
11437          * @param {Action} action The action that failed
11438          */
11439         actionfailed : true,
11440         /**
11441          * @event actioncomplete
11442          * Fires when an action is completed.
11443          * @param {Form} this
11444          * @param {Action} action The action that completed
11445          */
11446         actioncomplete : true
11447     });
11448 };
11449
11450 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11451
11452      /**
11453      * @cfg {String} method
11454      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11455      */
11456     method : 'POST',
11457     /**
11458      * @cfg {String} url
11459      * The URL to use for form actions if one isn't supplied in the action options.
11460      */
11461     /**
11462      * @cfg {Boolean} fileUpload
11463      * Set to true if this form is a file upload.
11464      */
11465
11466     /**
11467      * @cfg {Object} baseParams
11468      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11469      */
11470
11471     /**
11472      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11473      */
11474     timeout: 30,
11475     /**
11476      * @cfg {Sting} align (left|right) for navbar forms
11477      */
11478     align : 'left',
11479
11480     // private
11481     activeAction : null,
11482
11483     /**
11484      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11485      * element by passing it or its id or mask the form itself by passing in true.
11486      * @type Mixed
11487      */
11488     waitMsgTarget : false,
11489
11490     loadMask : true,
11491     
11492     /**
11493      * @cfg {Boolean} errorMask (true|false) default false
11494      */
11495     errorMask : false,
11496     
11497     /**
11498      * @cfg {Number} maskOffset Default 100
11499      */
11500     maskOffset : 100,
11501     
11502     /**
11503      * @cfg {Boolean} maskBody
11504      */
11505     maskBody : false,
11506
11507     getAutoCreate : function(){
11508
11509         var cfg = {
11510             tag: 'form',
11511             method : this.method || 'POST',
11512             id : this.id || Roo.id(),
11513             cls : ''
11514         };
11515         if (this.parent().xtype.match(/^Nav/)) {
11516             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11517
11518         }
11519
11520         if (this.labelAlign == 'left' ) {
11521             cfg.cls += ' form-horizontal';
11522         }
11523
11524
11525         return cfg;
11526     },
11527     initEvents : function()
11528     {
11529         this.el.on('submit', this.onSubmit, this);
11530         // this was added as random key presses on the form where triggering form submit.
11531         this.el.on('keypress', function(e) {
11532             if (e.getCharCode() != 13) {
11533                 return true;
11534             }
11535             // we might need to allow it for textareas.. and some other items.
11536             // check e.getTarget().
11537
11538             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11539                 return true;
11540             }
11541
11542             Roo.log("keypress blocked");
11543
11544             e.preventDefault();
11545             return false;
11546         });
11547         
11548     },
11549     // private
11550     onSubmit : function(e){
11551         e.stopEvent();
11552     },
11553
11554      /**
11555      * Returns true if client-side validation on the form is successful.
11556      * @return Boolean
11557      */
11558     isValid : function(){
11559         var items = this.getItems();
11560         var valid = true;
11561         var target = false;
11562         
11563         items.each(function(f){
11564             
11565             if(f.validate()){
11566                 return;
11567             }
11568             
11569             Roo.log('invalid field: ' + f.name);
11570             
11571             valid = false;
11572
11573             if(!target && f.el.isVisible(true)){
11574                 target = f;
11575             }
11576            
11577         });
11578         
11579         if(this.errorMask && !valid){
11580             Roo.bootstrap.form.Form.popover.mask(this, target);
11581         }
11582         
11583         return valid;
11584     },
11585     
11586     /**
11587      * Returns true if any fields in this form have changed since their original load.
11588      * @return Boolean
11589      */
11590     isDirty : function(){
11591         var dirty = false;
11592         var items = this.getItems();
11593         items.each(function(f){
11594            if(f.isDirty()){
11595                dirty = true;
11596                return false;
11597            }
11598            return true;
11599         });
11600         return dirty;
11601     },
11602      /**
11603      * Performs a predefined action (submit or load) or custom actions you define on this form.
11604      * @param {String} actionName The name of the action type
11605      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11606      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11607      * accept other config options):
11608      * <pre>
11609 Property          Type             Description
11610 ----------------  ---------------  ----------------------------------------------------------------------------------
11611 url               String           The url for the action (defaults to the form's url)
11612 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11613 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11614 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11615                                    validate the form on the client (defaults to false)
11616      * </pre>
11617      * @return {BasicForm} this
11618      */
11619     doAction : function(action, options){
11620         if(typeof action == 'string'){
11621             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11622         }
11623         if(this.fireEvent('beforeaction', this, action) !== false){
11624             this.beforeAction(action);
11625             action.run.defer(100, action);
11626         }
11627         return this;
11628     },
11629
11630     // private
11631     beforeAction : function(action){
11632         var o = action.options;
11633         
11634         if(this.loadMask){
11635             
11636             if(this.maskBody){
11637                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11638             } else {
11639                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11640             }
11641         }
11642         // not really supported yet.. ??
11643
11644         //if(this.waitMsgTarget === true){
11645         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11646         //}else if(this.waitMsgTarget){
11647         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11648         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11649         //}else {
11650         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11651        // }
11652
11653     },
11654
11655     // private
11656     afterAction : function(action, success){
11657         this.activeAction = null;
11658         var o = action.options;
11659
11660         if(this.loadMask){
11661             
11662             if(this.maskBody){
11663                 Roo.get(document.body).unmask();
11664             } else {
11665                 this.el.unmask();
11666             }
11667         }
11668         
11669         //if(this.waitMsgTarget === true){
11670 //            this.el.unmask();
11671         //}else if(this.waitMsgTarget){
11672         //    this.waitMsgTarget.unmask();
11673         //}else{
11674         //    Roo.MessageBox.updateProgress(1);
11675         //    Roo.MessageBox.hide();
11676        // }
11677         //
11678         if(success){
11679             if(o.reset){
11680                 this.reset();
11681             }
11682             Roo.callback(o.success, o.scope, [this, action]);
11683             this.fireEvent('actioncomplete', this, action);
11684
11685         }else{
11686
11687             // failure condition..
11688             // we have a scenario where updates need confirming.
11689             // eg. if a locking scenario exists..
11690             // we look for { errors : { needs_confirm : true }} in the response.
11691             if (
11692                 (typeof(action.result) != 'undefined')  &&
11693                 (typeof(action.result.errors) != 'undefined')  &&
11694                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11695            ){
11696                 var _t = this;
11697                 Roo.log("not supported yet");
11698                  /*
11699
11700                 Roo.MessageBox.confirm(
11701                     "Change requires confirmation",
11702                     action.result.errorMsg,
11703                     function(r) {
11704                         if (r != 'yes') {
11705                             return;
11706                         }
11707                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11708                     }
11709
11710                 );
11711                 */
11712
11713
11714                 return;
11715             }
11716
11717             Roo.callback(o.failure, o.scope, [this, action]);
11718             // show an error message if no failed handler is set..
11719             if (!this.hasListener('actionfailed')) {
11720                 Roo.log("need to add dialog support");
11721                 /*
11722                 Roo.MessageBox.alert("Error",
11723                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11724                         action.result.errorMsg :
11725                         "Saving Failed, please check your entries or try again"
11726                 );
11727                 */
11728             }
11729
11730             this.fireEvent('actionfailed', this, action);
11731         }
11732
11733     },
11734     /**
11735      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11736      * @param {String} id The value to search for
11737      * @return Field
11738      */
11739     findField : function(id){
11740         var items = this.getItems();
11741         var field = items.get(id);
11742         if(!field){
11743              items.each(function(f){
11744                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11745                     field = f;
11746                     return false;
11747                 }
11748                 return true;
11749             });
11750         }
11751         return field || null;
11752     },
11753      /**
11754      * Mark fields in this form invalid in bulk.
11755      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11756      * @return {BasicForm} this
11757      */
11758     markInvalid : function(errors){
11759         if(errors instanceof Array){
11760             for(var i = 0, len = errors.length; i < len; i++){
11761                 var fieldError = errors[i];
11762                 var f = this.findField(fieldError.id);
11763                 if(f){
11764                     f.markInvalid(fieldError.msg);
11765                 }
11766             }
11767         }else{
11768             var field, id;
11769             for(id in errors){
11770                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11771                     field.markInvalid(errors[id]);
11772                 }
11773             }
11774         }
11775         //Roo.each(this.childForms || [], function (f) {
11776         //    f.markInvalid(errors);
11777         //});
11778
11779         return this;
11780     },
11781
11782     /**
11783      * Set values for fields in this form in bulk.
11784      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11785      * @return {BasicForm} this
11786      */
11787     setValues : function(values){
11788         if(values instanceof Array){ // array of objects
11789             for(var i = 0, len = values.length; i < len; i++){
11790                 var v = values[i];
11791                 var f = this.findField(v.id);
11792                 if(f){
11793                     f.setValue(v.value);
11794                     if(this.trackResetOnLoad){
11795                         f.originalValue = f.getValue();
11796                     }
11797                 }
11798             }
11799         }else{ // object hash
11800             var field, id;
11801             for(id in values){
11802                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11803
11804                     if (field.setFromData &&
11805                         field.valueField &&
11806                         field.displayField &&
11807                         // combos' with local stores can
11808                         // be queried via setValue()
11809                         // to set their value..
11810                         (field.store && !field.store.isLocal)
11811                         ) {
11812                         // it's a combo
11813                         var sd = { };
11814                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11815                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11816                         field.setFromData(sd);
11817
11818                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11819                         
11820                         field.setFromData(values);
11821                         
11822                     } else {
11823                         field.setValue(values[id]);
11824                     }
11825
11826
11827                     if(this.trackResetOnLoad){
11828                         field.originalValue = field.getValue();
11829                     }
11830                 }
11831             }
11832         }
11833
11834         //Roo.each(this.childForms || [], function (f) {
11835         //    f.setValues(values);
11836         //});
11837
11838         return this;
11839     },
11840
11841     /**
11842      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11843      * they are returned as an array.
11844      * @param {Boolean} asString
11845      * @return {Object}
11846      */
11847     getValues : function(asString){
11848         //if (this.childForms) {
11849             // copy values from the child forms
11850         //    Roo.each(this.childForms, function (f) {
11851         //        this.setValues(f.getValues());
11852         //    }, this);
11853         //}
11854
11855
11856
11857         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11858         if(asString === true){
11859             return fs;
11860         }
11861         return Roo.urlDecode(fs);
11862     },
11863
11864     /**
11865      * Returns the fields in this form as an object with key/value pairs.
11866      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11867      * @return {Object}
11868      */
11869     getFieldValues : function(with_hidden)
11870     {
11871         var items = this.getItems();
11872         var ret = {};
11873         items.each(function(f){
11874             
11875             if (!f.getName()) {
11876                 return;
11877             }
11878             
11879             var v = f.getValue();
11880             
11881             if (f.inputType =='radio') {
11882                 if (typeof(ret[f.getName()]) == 'undefined') {
11883                     ret[f.getName()] = ''; // empty..
11884                 }
11885
11886                 if (!f.el.dom.checked) {
11887                     return;
11888
11889                 }
11890                 v = f.el.dom.value;
11891
11892             }
11893             
11894             if(f.xtype == 'MoneyField'){
11895                 ret[f.currencyName] = f.getCurrency();
11896             }
11897
11898             // not sure if this supported any more..
11899             if ((typeof(v) == 'object') && f.getRawValue) {
11900                 v = f.getRawValue() ; // dates..
11901             }
11902             // combo boxes where name != hiddenName...
11903             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11904                 ret[f.name] = f.getRawValue();
11905             }
11906             ret[f.getName()] = v;
11907         });
11908
11909         return ret;
11910     },
11911
11912     /**
11913      * Clears all invalid messages in this form.
11914      * @return {BasicForm} this
11915      */
11916     clearInvalid : function(){
11917         var items = this.getItems();
11918
11919         items.each(function(f){
11920            f.clearInvalid();
11921         });
11922
11923         return this;
11924     },
11925
11926     /**
11927      * Resets this form.
11928      * @return {BasicForm} this
11929      */
11930     reset : function(){
11931         var items = this.getItems();
11932         items.each(function(f){
11933             f.reset();
11934         });
11935
11936         Roo.each(this.childForms || [], function (f) {
11937             f.reset();
11938         });
11939
11940
11941         return this;
11942     },
11943     
11944     getItems : function()
11945     {
11946         var r=new Roo.util.MixedCollection(false, function(o){
11947             return o.id || (o.id = Roo.id());
11948         });
11949         var iter = function(el) {
11950             if (el.inputEl) {
11951                 r.add(el);
11952             }
11953             if (!el.items) {
11954                 return;
11955             }
11956             Roo.each(el.items,function(e) {
11957                 iter(e);
11958             });
11959         };
11960
11961         iter(this);
11962         return r;
11963     },
11964     
11965     hideFields : function(items)
11966     {
11967         Roo.each(items, function(i){
11968             
11969             var f = this.findField(i);
11970             
11971             if(!f){
11972                 return;
11973             }
11974             
11975             f.hide();
11976             
11977         }, this);
11978     },
11979     
11980     showFields : function(items)
11981     {
11982         Roo.each(items, function(i){
11983             
11984             var f = this.findField(i);
11985             
11986             if(!f){
11987                 return;
11988             }
11989             
11990             f.show();
11991             
11992         }, this);
11993     }
11994
11995 });
11996
11997 Roo.apply(Roo.bootstrap.form.Form, {
11998     
11999     popover : {
12000         
12001         padding : 5,
12002         
12003         isApplied : false,
12004         
12005         isMasked : false,
12006         
12007         form : false,
12008         
12009         target : false,
12010         
12011         toolTip : false,
12012         
12013         intervalID : false,
12014         
12015         maskEl : false,
12016         
12017         apply : function()
12018         {
12019             if(this.isApplied){
12020                 return;
12021             }
12022             
12023             this.maskEl = {
12024                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12025                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12026                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12027                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12028             };
12029             
12030             this.maskEl.top.enableDisplayMode("block");
12031             this.maskEl.left.enableDisplayMode("block");
12032             this.maskEl.bottom.enableDisplayMode("block");
12033             this.maskEl.right.enableDisplayMode("block");
12034             
12035             this.toolTip = new Roo.bootstrap.Tooltip({
12036                 cls : 'roo-form-error-popover',
12037                 alignment : {
12038                     'left' : ['r-l', [-2,0], 'right'],
12039                     'right' : ['l-r', [2,0], 'left'],
12040                     'bottom' : ['tl-bl', [0,2], 'top'],
12041                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12042                 }
12043             });
12044             
12045             this.toolTip.render(Roo.get(document.body));
12046
12047             this.toolTip.el.enableDisplayMode("block");
12048             
12049             Roo.get(document.body).on('click', function(){
12050                 this.unmask();
12051             }, this);
12052             
12053             Roo.get(document.body).on('touchstart', function(){
12054                 this.unmask();
12055             }, this);
12056             
12057             this.isApplied = true
12058         },
12059         
12060         mask : function(form, target)
12061         {
12062             this.form = form;
12063             
12064             this.target = target;
12065             
12066             if(!this.form.errorMask || !target.el){
12067                 return;
12068             }
12069             
12070             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12071             
12072             Roo.log(scrollable);
12073             
12074             var ot = this.target.el.calcOffsetsTo(scrollable);
12075             
12076             var scrollTo = ot[1] - this.form.maskOffset;
12077             
12078             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12079             
12080             scrollable.scrollTo('top', scrollTo);
12081             
12082             var box = this.target.el.getBox();
12083             Roo.log(box);
12084             var zIndex = Roo.bootstrap.Modal.zIndex++;
12085
12086             
12087             this.maskEl.top.setStyle('position', 'absolute');
12088             this.maskEl.top.setStyle('z-index', zIndex);
12089             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12090             this.maskEl.top.setLeft(0);
12091             this.maskEl.top.setTop(0);
12092             this.maskEl.top.show();
12093             
12094             this.maskEl.left.setStyle('position', 'absolute');
12095             this.maskEl.left.setStyle('z-index', zIndex);
12096             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12097             this.maskEl.left.setLeft(0);
12098             this.maskEl.left.setTop(box.y - this.padding);
12099             this.maskEl.left.show();
12100
12101             this.maskEl.bottom.setStyle('position', 'absolute');
12102             this.maskEl.bottom.setStyle('z-index', zIndex);
12103             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12104             this.maskEl.bottom.setLeft(0);
12105             this.maskEl.bottom.setTop(box.bottom + this.padding);
12106             this.maskEl.bottom.show();
12107
12108             this.maskEl.right.setStyle('position', 'absolute');
12109             this.maskEl.right.setStyle('z-index', zIndex);
12110             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12111             this.maskEl.right.setLeft(box.right + this.padding);
12112             this.maskEl.right.setTop(box.y - this.padding);
12113             this.maskEl.right.show();
12114
12115             this.toolTip.bindEl = this.target.el;
12116
12117             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12118
12119             var tip = this.target.blankText;
12120
12121             if(this.target.getValue() !== '' ) {
12122                 
12123                 if (this.target.invalidText.length) {
12124                     tip = this.target.invalidText;
12125                 } else if (this.target.regexText.length){
12126                     tip = this.target.regexText;
12127                 }
12128             }
12129
12130             this.toolTip.show(tip);
12131
12132             this.intervalID = window.setInterval(function() {
12133                 Roo.bootstrap.form.Form.popover.unmask();
12134             }, 10000);
12135
12136             window.onwheel = function(){ return false;};
12137             
12138             (function(){ this.isMasked = true; }).defer(500, this);
12139             
12140         },
12141         
12142         unmask : function()
12143         {
12144             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12145                 return;
12146             }
12147             
12148             this.maskEl.top.setStyle('position', 'absolute');
12149             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12150             this.maskEl.top.hide();
12151
12152             this.maskEl.left.setStyle('position', 'absolute');
12153             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12154             this.maskEl.left.hide();
12155
12156             this.maskEl.bottom.setStyle('position', 'absolute');
12157             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12158             this.maskEl.bottom.hide();
12159
12160             this.maskEl.right.setStyle('position', 'absolute');
12161             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12162             this.maskEl.right.hide();
12163             
12164             this.toolTip.hide();
12165             
12166             this.toolTip.el.hide();
12167             
12168             window.onwheel = function(){ return true;};
12169             
12170             if(this.intervalID){
12171                 window.clearInterval(this.intervalID);
12172                 this.intervalID = false;
12173             }
12174             
12175             this.isMasked = false;
12176             
12177         }
12178         
12179     }
12180     
12181 });
12182
12183 /*
12184  * Based on:
12185  * Ext JS Library 1.1.1
12186  * Copyright(c) 2006-2007, Ext JS, LLC.
12187  *
12188  * Originally Released Under LGPL - original licence link has changed is not relivant.
12189  *
12190  * Fork - LGPL
12191  * <script type="text/javascript">
12192  */
12193 /**
12194  * @class Roo.form.VTypes
12195  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12196  * @static
12197  */
12198 Roo.form.VTypes = function(){
12199     // closure these in so they are only created once.
12200     var alpha = /^[a-zA-Z_]+$/;
12201     var alphanum = /^[a-zA-Z0-9_]+$/;
12202     var email = /^([\w-]+)(\.[\w-]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12203     var url = /^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12204     var urlWeb = /^((https?):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12205
12206     // All these messages and functions are configurable
12207     return {
12208         /**
12209          * The function used to validate email addresses
12210          * @param {String} value The email address
12211          */
12212         email : function(v){
12213             return email.test(v);
12214         },
12215         /**
12216          * The error text to display when the email validation function returns false
12217          * @type String
12218          */
12219         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12220         /**
12221          * The keystroke filter mask to be applied on email input
12222          * @type RegExp
12223          */
12224         emailMask : /[a-z0-9_\.\-@]/i,
12225
12226         /**
12227          * The function used to validate URLs
12228          * @param {String} value The URL
12229          */
12230         url : function(v){
12231             return url.test(v);
12232         },
12233         /**
12234          * The funciton used to validate URLs (only allow schemes 'https' and 'http')
12235          * @param {String} v The URL
12236          */
12237         urlWeb : function(v) {
12238             return urlWeb.test(v);
12239         },
12240         /**
12241          * The error text to display when the url validation function returns false
12242          * @type String
12243          */
12244         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12245         
12246         /**
12247          * The function used to validate alpha values
12248          * @param {String} value The value
12249          */
12250         alpha : function(v){
12251             return alpha.test(v);
12252         },
12253         /**
12254          * The error text to display when the alpha validation function returns false
12255          * @type String
12256          */
12257         alphaText : 'This field should only contain letters and _',
12258         /**
12259          * The keystroke filter mask to be applied on alpha input
12260          * @type RegExp
12261          */
12262         alphaMask : /[a-z_]/i,
12263
12264         /**
12265          * The function used to validate alphanumeric values
12266          * @param {String} value The value
12267          */
12268         alphanum : function(v){
12269             return alphanum.test(v);
12270         },
12271         /**
12272          * The error text to display when the alphanumeric validation function returns false
12273          * @type String
12274          */
12275         alphanumText : 'This field should only contain letters, numbers and _',
12276         /**
12277          * The keystroke filter mask to be applied on alphanumeric input
12278          * @type RegExp
12279          */
12280         alphanumMask : /[a-z0-9_]/i
12281     };
12282 }();/*
12283  * - LGPL
12284  *
12285  * Input
12286  * 
12287  */
12288
12289 /**
12290  * @class Roo.bootstrap.form.Input
12291  * @extends Roo.bootstrap.Component
12292  * Bootstrap Input class
12293  * @cfg {Boolean} disabled is it disabled
12294  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12295  * @cfg {String} name name of the input
12296  * @cfg {string} fieldLabel - the label associated
12297  * @cfg {string} placeholder - placeholder to put in text.
12298  * @cfg {string} before - input group add on before
12299  * @cfg {string} after - input group add on after
12300  * @cfg {string} size - (lg|sm) or leave empty..
12301  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12302  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12303  * @cfg {Number} md colspan out of 12 for computer-sized screens
12304  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12305  * @cfg {string} value default value of the input
12306  * @cfg {Number} labelWidth set the width of label 
12307  * @cfg {Number} labellg set the width of label (1-12)
12308  * @cfg {Number} labelmd set the width of label (1-12)
12309  * @cfg {Number} labelsm set the width of label (1-12)
12310  * @cfg {Number} labelxs set the width of label (1-12)
12311  * @cfg {String} labelAlign (top|left)
12312  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12313  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12314  * @cfg {String} indicatorpos (left|right) default left
12315  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12316  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12317  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12318  * @cfg {Roo.bootstrap.Button} before Button to show before
12319  * @cfg {Roo.bootstrap.Button} afterButton to show before
12320  * @cfg {String} align (left|center|right) Default left
12321  * @cfg {Boolean} forceFeedback (true|false) Default false
12322  * 
12323  * @constructor
12324  * Create a new Input
12325  * @param {Object} config The config object
12326  */
12327
12328 Roo.bootstrap.form.Input = function(config){
12329     
12330     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12331     
12332     this.addEvents({
12333         /**
12334          * @event focus
12335          * Fires when this field receives input focus.
12336          * @param {Roo.form.Field} this
12337          */
12338         focus : true,
12339         /**
12340          * @event blur
12341          * Fires when this field loses input focus.
12342          * @param {Roo.form.Field} this
12343          */
12344         blur : true,
12345         /**
12346          * @event specialkey
12347          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12348          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12349          * @param {Roo.form.Field} this
12350          * @param {Roo.EventObject} e The event object
12351          */
12352         specialkey : true,
12353         /**
12354          * @event change
12355          * Fires just before the field blurs if the field value has changed.
12356          * @param {Roo.form.Field} this
12357          * @param {Mixed} newValue The new value
12358          * @param {Mixed} oldValue The original value
12359          */
12360         change : true,
12361         /**
12362          * @event invalid
12363          * Fires after the field has been marked as invalid.
12364          * @param {Roo.form.Field} this
12365          * @param {String} msg The validation message
12366          */
12367         invalid : true,
12368         /**
12369          * @event valid
12370          * Fires after the field has been validated with no errors.
12371          * @param {Roo.form.Field} this
12372          */
12373         valid : true,
12374          /**
12375          * @event keyup
12376          * Fires after the key up
12377          * @param {Roo.form.Field} this
12378          * @param {Roo.EventObject}  e The event Object
12379          */
12380         keyup : true,
12381         /**
12382          * @event paste
12383          * Fires after the user pastes into input
12384          * @param {Roo.form.Field} this
12385          * @param {Roo.EventObject}  e The event Object
12386          */
12387         paste : true
12388     });
12389 };
12390
12391 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12392      /**
12393      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12394       automatic validation (defaults to "keyup").
12395      */
12396     validationEvent : "keyup",
12397      /**
12398      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12399      */
12400     validateOnBlur : true,
12401     /**
12402      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12403      */
12404     validationDelay : 250,
12405      /**
12406      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12407      */
12408     focusClass : "x-form-focus",  // not needed???
12409     
12410        
12411     /**
12412      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12413      */
12414     invalidClass : "has-warning",
12415     
12416     /**
12417      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12418      */
12419     validClass : "has-success",
12420     
12421     /**
12422      * @cfg {Boolean} hasFeedback (true|false) default true
12423      */
12424     hasFeedback : true,
12425     
12426     /**
12427      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12428      */
12429     invalidFeedbackClass : "glyphicon-warning-sign",
12430     
12431     /**
12432      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12433      */
12434     validFeedbackClass : "glyphicon-ok",
12435     
12436     /**
12437      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12438      */
12439     selectOnFocus : false,
12440     
12441      /**
12442      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12443      */
12444     maskRe : null,
12445        /**
12446      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12447      */
12448     vtype : null,
12449     
12450       /**
12451      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12452      */
12453     disableKeyFilter : false,
12454     
12455        /**
12456      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12457      */
12458     disabled : false,
12459      /**
12460      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12461      */
12462     allowBlank : true,
12463     /**
12464      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12465      */
12466     blankText : "Please complete this mandatory field",
12467     
12468      /**
12469      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12470      */
12471     minLength : 0,
12472     /**
12473      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12474      */
12475     maxLength : Number.MAX_VALUE,
12476     /**
12477      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12478      */
12479     minLengthText : "The minimum length for this field is {0}",
12480     /**
12481      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12482      */
12483     maxLengthText : "The maximum length for this field is {0}",
12484   
12485     
12486     /**
12487      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12488      * If available, this function will be called only after the basic validators all return true, and will be passed the
12489      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12490      */
12491     validator : null,
12492     /**
12493      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12494      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12495      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12496      */
12497     regex : null,
12498     /**
12499      * @cfg {String} regexText -- Depricated - use Invalid Text
12500      */
12501     regexText : "",
12502     
12503     /**
12504      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12505      */
12506     invalidText : "",
12507     
12508     
12509     
12510     autocomplete: false,
12511     
12512     
12513     fieldLabel : '',
12514     inputType : 'text',
12515     
12516     name : false,
12517     placeholder: false,
12518     before : false,
12519     after : false,
12520     size : false,
12521     hasFocus : false,
12522     preventMark: false,
12523     isFormField : true,
12524     value : '',
12525     labelWidth : 2,
12526     labelAlign : false,
12527     readOnly : false,
12528     align : false,
12529     formatedValue : false,
12530     forceFeedback : false,
12531     
12532     indicatorpos : 'left',
12533     
12534     labellg : 0,
12535     labelmd : 0,
12536     labelsm : 0,
12537     labelxs : 0,
12538     
12539     capture : '',
12540     accept : '',
12541     
12542     parentLabelAlign : function()
12543     {
12544         var parent = this;
12545         while (parent.parent()) {
12546             parent = parent.parent();
12547             if (typeof(parent.labelAlign) !='undefined') {
12548                 return parent.labelAlign;
12549             }
12550         }
12551         return 'left';
12552         
12553     },
12554     
12555     getAutoCreate : function()
12556     {
12557         
12558         var id = Roo.id();
12559         
12560         var cfg = {};
12561         
12562         if(this.inputType != 'hidden'){
12563             cfg.cls = 'form-group' //input-group
12564         }
12565         
12566         var input =  {
12567             tag: 'input',
12568             id : id,
12569             type : this.inputType,
12570             value : this.value,
12571             cls : 'form-control',
12572             placeholder : this.placeholder || '',
12573             autocomplete : this.autocomplete || 'new-password'
12574         };
12575         if (this.inputType == 'file') {
12576             input.style = 'overflow:hidden'; // why not in CSS?
12577         }
12578         
12579         if(this.capture.length){
12580             input.capture = this.capture;
12581         }
12582         
12583         if(this.accept.length){
12584             input.accept = this.accept + "/*";
12585         }
12586         
12587         if(this.align){
12588             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12589         }
12590         
12591         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12592             input.maxLength = this.maxLength;
12593         }
12594         
12595         if (this.disabled) {
12596             input.disabled=true;
12597         }
12598         
12599         if (this.readOnly) {
12600             input.readonly=true;
12601         }
12602         
12603         if (this.name) {
12604             input.name = this.name;
12605         }
12606         
12607         if (this.size) {
12608             input.cls += ' input-' + this.size;
12609         }
12610         
12611         var settings=this;
12612         ['xs','sm','md','lg'].map(function(size){
12613             if (settings[size]) {
12614                 cfg.cls += ' col-' + size + '-' + settings[size];
12615             }
12616         });
12617         
12618         var inputblock = input;
12619         
12620         var feedback = {
12621             tag: 'span',
12622             cls: 'glyphicon form-control-feedback'
12623         };
12624             
12625         if(this.hasFeedback && this.inputType != 'hidden'){
12626             
12627             inputblock = {
12628                 cls : 'has-feedback',
12629                 cn :  [
12630                     input,
12631                     feedback
12632                 ] 
12633             };  
12634         }
12635         
12636         if (this.before || this.after) {
12637             
12638             inputblock = {
12639                 cls : 'input-group',
12640                 cn :  [] 
12641             };
12642             
12643             if (this.before && typeof(this.before) == 'string') {
12644                 
12645                 inputblock.cn.push({
12646                     tag :'span',
12647                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12648                     html : this.before
12649                 });
12650             }
12651             if (this.before && typeof(this.before) == 'object') {
12652                 this.before = Roo.factory(this.before);
12653                 
12654                 inputblock.cn.push({
12655                     tag :'span',
12656                     cls : 'roo-input-before input-group-prepend   input-group-' +
12657                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12658                 });
12659             }
12660             
12661             inputblock.cn.push(input);
12662             
12663             if (this.after && typeof(this.after) == 'string') {
12664                 inputblock.cn.push({
12665                     tag :'span',
12666                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12667                     html : this.after
12668                 });
12669             }
12670             if (this.after && typeof(this.after) == 'object') {
12671                 this.after = Roo.factory(this.after);
12672                 
12673                 inputblock.cn.push({
12674                     tag :'span',
12675                     cls : 'roo-input-after input-group-append  input-group-' +
12676                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12677                 });
12678             }
12679             
12680             if(this.hasFeedback && this.inputType != 'hidden'){
12681                 inputblock.cls += ' has-feedback';
12682                 inputblock.cn.push(feedback);
12683             }
12684         };
12685         
12686         
12687         
12688         cfg = this.getAutoCreateLabel( cfg, inputblock );
12689         
12690        
12691          
12692         
12693         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12694            cfg.cls += ' navbar-form';
12695         }
12696         
12697         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12698             // on BS4 we do this only if not form 
12699             cfg.cls += ' navbar-form';
12700             cfg.tag = 'li';
12701         }
12702         
12703         return cfg;
12704         
12705     },
12706     /**
12707      * autocreate the label - also used by textara... ?? and others?
12708      */
12709     getAutoCreateLabel : function( cfg, inputblock )
12710     {
12711         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12712        
12713         var indicator = {
12714             tag : 'i',
12715             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12716             tooltip : 'This field is required'
12717         };
12718         if (this.allowBlank ) {
12719             indicator.style = this.allowBlank ? ' display:none' : '';
12720         }
12721         if (align ==='left' && this.fieldLabel.length) {
12722             
12723             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12724             
12725             cfg.cn = [
12726                 indicator,
12727                 {
12728                     tag: 'label',
12729                     'for' :  id,
12730                     cls : 'control-label col-form-label',
12731                     html : this.fieldLabel
12732
12733                 },
12734                 {
12735                     cls : "", 
12736                     cn: [
12737                         inputblock
12738                     ]
12739                 }
12740             ];
12741             
12742             var labelCfg = cfg.cn[1];
12743             var contentCfg = cfg.cn[2];
12744             
12745             if(this.indicatorpos == 'right'){
12746                 cfg.cn = [
12747                     {
12748                         tag: 'label',
12749                         'for' :  id,
12750                         cls : 'control-label col-form-label',
12751                         cn : [
12752                             {
12753                                 tag : 'span',
12754                                 html : this.fieldLabel
12755                             },
12756                             indicator
12757                         ]
12758                     },
12759                     {
12760                         cls : "",
12761                         cn: [
12762                             inputblock
12763                         ]
12764                     }
12765
12766                 ];
12767                 
12768                 labelCfg = cfg.cn[0];
12769                 contentCfg = cfg.cn[1];
12770             
12771             }
12772             
12773             if(this.labelWidth > 12){
12774                 labelCfg.style = "width: " + this.labelWidth + 'px';
12775             }
12776             
12777             if(this.labelWidth < 13 && this.labelmd == 0){
12778                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12779             }
12780             
12781             if(this.labellg > 0){
12782                 labelCfg.cls += ' col-lg-' + this.labellg;
12783                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12784             }
12785             
12786             if(this.labelmd > 0){
12787                 labelCfg.cls += ' col-md-' + this.labelmd;
12788                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12789             }
12790             
12791             if(this.labelsm > 0){
12792                 labelCfg.cls += ' col-sm-' + this.labelsm;
12793                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12794             }
12795             
12796             if(this.labelxs > 0){
12797                 labelCfg.cls += ' col-xs-' + this.labelxs;
12798                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12799             }
12800             
12801             
12802         } else if ( this.fieldLabel.length) {
12803                 
12804             
12805             
12806             cfg.cn = [
12807                 {
12808                     tag : 'i',
12809                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12810                     tooltip : 'This field is required',
12811                     style : this.allowBlank ? ' display:none' : '' 
12812                 },
12813                 {
12814                     tag: 'label',
12815                    //cls : 'input-group-addon',
12816                     html : this.fieldLabel
12817
12818                 },
12819
12820                inputblock
12821
12822            ];
12823            
12824            if(this.indicatorpos == 'right'){
12825        
12826                 cfg.cn = [
12827                     {
12828                         tag: 'label',
12829                        //cls : 'input-group-addon',
12830                         html : this.fieldLabel
12831
12832                     },
12833                     {
12834                         tag : 'i',
12835                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12836                         tooltip : 'This field is required',
12837                         style : this.allowBlank ? ' display:none' : '' 
12838                     },
12839
12840                    inputblock
12841
12842                ];
12843
12844             }
12845
12846         } else {
12847             
12848             cfg.cn = [
12849
12850                     inputblock
12851
12852             ];
12853                 
12854                 
12855         };
12856         return cfg;
12857     },
12858     
12859     
12860     /**
12861      * return the real input element.
12862      */
12863     inputEl: function ()
12864     {
12865         return this.el.select('input.form-control',true).first();
12866     },
12867     
12868     tooltipEl : function()
12869     {
12870         return this.inputEl();
12871     },
12872     
12873     indicatorEl : function()
12874     {
12875         if (Roo.bootstrap.version == 4) {
12876             return false; // not enabled in v4 yet.
12877         }
12878         
12879         var indicator = this.el.select('i.roo-required-indicator',true).first();
12880         
12881         if(!indicator){
12882             return false;
12883         }
12884         
12885         return indicator;
12886         
12887     },
12888     
12889     setDisabled : function(v)
12890     {
12891         var i  = this.inputEl().dom;
12892         if (!v) {
12893             i.removeAttribute('disabled');
12894             return;
12895             
12896         }
12897         i.setAttribute('disabled','true');
12898     },
12899     initEvents : function()
12900     {
12901           
12902         this.inputEl().on("keydown" , this.fireKey,  this);
12903         this.inputEl().on("focus", this.onFocus,  this);
12904         this.inputEl().on("blur", this.onBlur,  this);
12905         
12906         this.inputEl().relayEvent('keyup', this);
12907         this.inputEl().relayEvent('paste', this);
12908         
12909         this.indicator = this.indicatorEl();
12910         
12911         if(this.indicator){
12912             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12913         }
12914  
12915         // reference to original value for reset
12916         this.originalValue = this.getValue();
12917         //Roo.form.TextField.superclass.initEvents.call(this);
12918         if(this.validationEvent == 'keyup'){
12919             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12920             this.inputEl().on('keyup', this.filterValidation, this);
12921         }
12922         else if(this.validationEvent !== false){
12923             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12924         }
12925         
12926         if(this.selectOnFocus){
12927             this.on("focus", this.preFocus, this);
12928             
12929         }
12930         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12931             this.inputEl().on("keypress", this.filterKeys, this);
12932         } else {
12933             this.inputEl().relayEvent('keypress', this);
12934         }
12935        /* if(this.grow){
12936             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12937             this.el.on("click", this.autoSize,  this);
12938         }
12939         */
12940         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12941             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12942         }
12943         
12944         if (typeof(this.before) == 'object') {
12945             this.before.render(this.el.select('.roo-input-before',true).first());
12946         }
12947         if (typeof(this.after) == 'object') {
12948             this.after.render(this.el.select('.roo-input-after',true).first());
12949         }
12950         
12951         this.inputEl().on('change', this.onChange, this);
12952
12953         if(this.hasFeedback && this.inputType != 'hidden'){
12954             
12955             var feedback = this.el.select('.form-control-feedback', true).first();
12956
12957             if(feedback) {
12958                 feedback.hide();
12959             }
12960         }
12961         
12962     },
12963     filterValidation : function(e){
12964         if(!e.isNavKeyPress()){
12965             this.validationTask.delay(this.validationDelay);
12966         }
12967     },
12968      /**
12969      * Validates the field value
12970      * @return {Boolean} True if the value is valid, else false
12971      */
12972     validate : function(){
12973         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12974         if(this.disabled || this.validateValue(this.getRawValue())){
12975             this.markValid();
12976             return true;
12977         }
12978         
12979         this.markInvalid();
12980         return false;
12981     },
12982     
12983     
12984     /**
12985      * Validates a value according to the field's validation rules and marks the field as invalid
12986      * if the validation fails
12987      * @param {Mixed} value The value to validate
12988      * @return {Boolean} True if the value is valid, else false
12989      */
12990     validateValue : function(value)
12991     {
12992         if(this.getVisibilityEl().hasClass('hidden')){
12993             return true;
12994         }
12995         
12996         if(value.length < 1)  { // if it's blank
12997             if(this.allowBlank){
12998                 return true;
12999             }
13000             return false;
13001         }
13002         
13003         if(value.length < this.minLength){
13004             return false;
13005         }
13006         if(value.length > this.maxLength){
13007             return false;
13008         }
13009         if(this.vtype){
13010             var vt = Roo.form.VTypes;
13011             if(!vt[this.vtype](value, this)){
13012                 return false;
13013             }
13014         }
13015         if(typeof this.validator == "function"){
13016             var msg = this.validator(value);
13017             if (typeof(msg) == 'string') {
13018                 this.invalidText = msg;
13019             }
13020             if(msg !== true){
13021                 return false;
13022             }
13023         }
13024         
13025         if(this.regex && !this.regex.test(value)){
13026             return false;
13027         }
13028         
13029         return true;
13030     },
13031     
13032      // private
13033     fireKey : function(e){
13034         //Roo.log('field ' + e.getKey());
13035         if(e.isNavKeyPress()){
13036             this.fireEvent("specialkey", this, e);
13037         }
13038     },
13039     focus : function (selectText){
13040         if(this.rendered){
13041             this.inputEl().focus();
13042             if(selectText === true){
13043                 this.inputEl().dom.select();
13044             }
13045         }
13046         return this;
13047     } ,
13048     
13049     onFocus : function(){
13050         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13051            // this.el.addClass(this.focusClass);
13052         }
13053         if(!this.hasFocus){
13054             this.hasFocus = true;
13055             this.startValue = this.getValue();
13056             this.fireEvent("focus", this);
13057         }
13058     },
13059     
13060     beforeBlur : Roo.emptyFn,
13061
13062     
13063     // private
13064     onBlur : function(){
13065         this.beforeBlur();
13066         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13067             //this.el.removeClass(this.focusClass);
13068         }
13069         this.hasFocus = false;
13070         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13071             this.validate();
13072         }
13073         var v = this.getValue();
13074         if(String(v) !== String(this.startValue)){
13075             this.fireEvent('change', this, v, this.startValue);
13076         }
13077         this.fireEvent("blur", this);
13078     },
13079     
13080     onChange : function(e)
13081     {
13082         var v = this.getValue();
13083         if(String(v) !== String(this.startValue)){
13084             this.fireEvent('change', this, v, this.startValue);
13085         }
13086         
13087     },
13088     
13089     /**
13090      * Resets the current field value to the originally loaded value and clears any validation messages
13091      */
13092     reset : function(){
13093         this.setValue(this.originalValue);
13094         // this.validate();
13095         this.el.removeClass([this.invalidClass, this.validClass]);
13096         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13097
13098         if(this.hasFeedback && this.inputType != 'hidden'){
13099             
13100             var feedback = this.el.select('.form-control-feedback', true).first();
13101             
13102             if(feedback){
13103                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13104                 feedback.update('');
13105                 feedback.hide();
13106             }
13107             
13108         }
13109     },
13110      /**
13111      * Returns the name of the field
13112      * @return {Mixed} name The name field
13113      */
13114     getName: function(){
13115         return this.name;
13116     },
13117      /**
13118      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13119      * @return {Mixed} value The field value
13120      */
13121     getValue : function(){
13122         var v = this.inputEl().getValue();
13123         return v;
13124     },
13125     /**
13126      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13127      * @return {Mixed} value The field value
13128      */
13129     getRawValue : function(){
13130         var v = this.inputEl().getValue();
13131         
13132         return v;
13133     },
13134     
13135     /**
13136      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13137      * @param {Mixed} value The value to set
13138      */
13139     setRawValue : function(v){
13140         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13141     },
13142     
13143     selectText : function(start, end){
13144         var v = this.getRawValue();
13145         if(v.length > 0){
13146             start = start === undefined ? 0 : start;
13147             end = end === undefined ? v.length : end;
13148             var d = this.inputEl().dom;
13149             if(d.setSelectionRange){
13150                 d.setSelectionRange(start, end);
13151             }else if(d.createTextRange){
13152                 var range = d.createTextRange();
13153                 range.moveStart("character", start);
13154                 range.moveEnd("character", v.length-end);
13155                 range.select();
13156             }
13157         }
13158     },
13159     
13160     /**
13161      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13162      * @param {Mixed} value The value to set
13163      */
13164     setValue : function(v){
13165         this.value = v;
13166         if(this.rendered){
13167             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13168             this.validate();
13169         }
13170     },
13171     
13172     /*
13173     processValue : function(value){
13174         if(this.stripCharsRe){
13175             var newValue = value.replace(this.stripCharsRe, '');
13176             if(newValue !== value){
13177                 this.setRawValue(newValue);
13178                 return newValue;
13179             }
13180         }
13181         return value;
13182     },
13183   */
13184     preFocus : function(){
13185         
13186         if(this.selectOnFocus){
13187             this.inputEl().dom.select();
13188         }
13189     },
13190     filterKeys : function(e){
13191         var k = e.getKey();
13192         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13193             return;
13194         }
13195         var c = e.getCharCode(), cc = String.fromCharCode(c);
13196         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13197             return;
13198         }
13199         if(!this.maskRe.test(cc)){
13200             e.stopEvent();
13201         }
13202     },
13203      /**
13204      * Clear any invalid styles/messages for this field
13205      */
13206     clearInvalid : function(){
13207         
13208         if(!this.el || this.preventMark){ // not rendered
13209             return;
13210         }
13211         
13212         
13213         this.inputEl().removeClass([this.invalidClass, 'is-invalid']);
13214         
13215         if(this.hasFeedback && this.inputType != 'hidden'){
13216             
13217             var feedback = this.el.select('.form-control-feedback', true).first();
13218             
13219             if(feedback){
13220                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13221
13222                 feedback.update('');
13223                 feedback.hide();
13224             }
13225             
13226         }
13227         
13228         if(this.indicator){
13229             this.indicator.removeClass('visible');
13230             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13231         }
13232         
13233         this.fireEvent('valid', this);
13234     },
13235     
13236      /**
13237      * Mark this field as valid
13238      */
13239     markValid : function()
13240     {   
13241         if(!this.el  || this.preventMark){ // not rendered...
13242             return;
13243         }
13244         
13245         this.el.removeClass([this.invalidClass, this.validClass]);
13246         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13247
13248         var feedback = this.el.select('.form-control-feedback', true).first();
13249             
13250         if(feedback){
13251             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13252             feedback.update('');
13253             feedback.hide();
13254         }
13255         
13256         if(this.indicator){
13257             this.indicator.removeClass('visible');
13258             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13259         }
13260         
13261         if(this.disabled){
13262             return;
13263         }
13264         
13265            
13266         if(this.allowBlank && !this.getRawValue().length){
13267             return;
13268         }
13269         if (Roo.bootstrap.version == 3) {
13270             this.el.addClass(this.validClass);
13271         } else {
13272             this.inputEl().addClass('is-valid');
13273         }
13274
13275         if(this.hasFeedback && this.inputType != 'hidden'){
13276             
13277             var feedback = this.el.select('.form-control-feedback', true).first();
13278             
13279             if(feedback){
13280                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13281                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13282             }
13283             
13284         }
13285         
13286         this.fireEvent('valid', this);
13287     },
13288     
13289      /**
13290      * Mark this field as invalid
13291      * @param {String} msg The validation message
13292      */
13293     markInvalid : function(msg)
13294     {
13295         if(!this.el  || this.preventMark){ // not rendered
13296             return;
13297         }
13298         
13299         this.el.removeClass([this.invalidClass, this.validClass]);
13300         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13301         
13302         var feedback = this.el.select('.form-control-feedback', true).first();
13303             
13304         if(feedback){
13305             this.el.select('.form-control-feedback', true).first().removeClass(
13306                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13307             feedback.update('');
13308             feedback.hide();
13309         }
13310
13311         if(this.disabled){
13312             return;
13313         }
13314         
13315         if(this.allowBlank && !this.getRawValue().length){
13316             return;
13317         }
13318         
13319         if(this.indicator){
13320             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13321             this.indicator.addClass('visible');
13322         }
13323         if (Roo.bootstrap.version == 3) {
13324             this.el.addClass(this.invalidClass);
13325         } else {
13326             this.inputEl().addClass('is-invalid');
13327         }
13328         
13329         
13330         
13331         if(this.hasFeedback && this.inputType != 'hidden'){
13332             
13333             var feedback = this.el.select('.form-control-feedback', true).first();
13334             
13335             if(feedback){
13336                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13337                 
13338                 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13339
13340                 feedback.update(typeof(msg) == 'undefined' ? this.invalidText : msg);
13341
13342                 if(!this.allowBlank && !this.getRawValue().length){
13343                     feedback.update(this.blankText);
13344                 }
13345
13346                 if(feedback.dom.innerHTML) {
13347                     feedback.show();
13348                 }
13349                 
13350             }
13351             
13352         }
13353         
13354         this.fireEvent('invalid', this, msg);
13355     },
13356     // private
13357     SafariOnKeyDown : function(event)
13358     {
13359         // this is a workaround for a password hang bug on chrome/ webkit.
13360         if (this.inputEl().dom.type != 'password') {
13361             return;
13362         }
13363         
13364         var isSelectAll = false;
13365         
13366         if(this.inputEl().dom.selectionEnd > 0){
13367             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13368         }
13369         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13370             event.preventDefault();
13371             this.setValue('');
13372             return;
13373         }
13374         
13375         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13376             
13377             event.preventDefault();
13378             // this is very hacky as keydown always get's upper case.
13379             //
13380             var cc = String.fromCharCode(event.getCharCode());
13381             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13382             
13383         }
13384     },
13385     adjustWidth : function(tag, w){
13386         tag = tag.toLowerCase();
13387         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13388             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13389                 if(tag == 'input'){
13390                     return w + 2;
13391                 }
13392                 if(tag == 'textarea'){
13393                     return w-2;
13394                 }
13395             }else if(Roo.isOpera){
13396                 if(tag == 'input'){
13397                     return w + 2;
13398                 }
13399                 if(tag == 'textarea'){
13400                     return w-2;
13401                 }
13402             }
13403         }
13404         return w;
13405     },
13406     
13407     setFieldLabel : function(v)
13408     {
13409         if(!this.rendered){
13410             return;
13411         }
13412         
13413         if(this.indicatorEl()){
13414             var ar = this.el.select('label > span',true);
13415             
13416             if (ar.elements.length) {
13417                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13418                 this.fieldLabel = v;
13419                 return;
13420             }
13421             
13422             var br = this.el.select('label',true);
13423             
13424             if(br.elements.length) {
13425                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13426                 this.fieldLabel = v;
13427                 return;
13428             }
13429             
13430             Roo.log('Cannot Found any of label > span || label in input');
13431             return;
13432         }
13433         
13434         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13435         this.fieldLabel = v;
13436         
13437         
13438     }
13439 });
13440
13441  
13442 /*
13443  * - LGPL
13444  *
13445  * Input
13446  * 
13447  */
13448
13449 /**
13450  * @class Roo.bootstrap.form.TextArea
13451  * @extends Roo.bootstrap.form.Input
13452  * Bootstrap TextArea class
13453  * @cfg {Number} cols Specifies the visible width of a text area
13454  * @cfg {Number} rows Specifies the visible number of lines in a text area
13455  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13456  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13457  * @cfg {string} html text
13458  * 
13459  * @constructor
13460  * Create a new TextArea
13461  * @param {Object} config The config object
13462  */
13463
13464 Roo.bootstrap.form.TextArea = function(config){
13465     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13466    
13467 };
13468
13469 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13470      
13471     cols : false,
13472     rows : 5,
13473     readOnly : false,
13474     warp : 'soft',
13475     resize : false,
13476     value: false,
13477     html: false,
13478     
13479     getAutoCreate : function(){
13480         
13481         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13482         
13483         var id = Roo.id();
13484         
13485         var cfg = {};
13486         
13487         if(this.inputType != 'hidden'){
13488             cfg.cls = 'form-group' //input-group
13489         }
13490         
13491         var input =  {
13492             tag: 'textarea',
13493             id : id,
13494             warp : this.warp,
13495             rows : this.rows,
13496             value : this.value || '',
13497             html: this.html || '',
13498             cls : 'form-control',
13499             placeholder : this.placeholder || '' 
13500             
13501         };
13502         
13503         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13504             input.maxLength = this.maxLength;
13505         }
13506         
13507         if(this.resize){
13508             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13509         }
13510         
13511         if(this.cols){
13512             input.cols = this.cols;
13513         }
13514         
13515         if (this.readOnly) {
13516             input.readonly = true;
13517         }
13518         
13519         if (this.name) {
13520             input.name = this.name;
13521         }
13522         
13523         if (this.size) {
13524             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13525         }
13526         
13527         var settings=this;
13528         ['xs','sm','md','lg'].map(function(size){
13529             if (settings[size]) {
13530                 cfg.cls += ' col-' + size + '-' + settings[size];
13531             }
13532         });
13533         
13534         var inputblock = input;
13535         
13536         if(this.hasFeedback){
13537             
13538             var feedback = {
13539                 tag: 'span',
13540                 cls: 'glyphicon form-control-feedback'
13541             };
13542
13543             inputblock = {
13544                 cls : 'has-feedback',
13545                 cn :  [
13546                     input,
13547                     feedback
13548                 ] 
13549             };  
13550         }
13551         
13552         
13553         if (this.before || this.after) {
13554             
13555             inputblock = {
13556                 cls : 'input-group',
13557                 cn :  [] 
13558             };
13559             if (this.before) {
13560                 inputblock.cn.push({
13561                     tag :'span',
13562                     cls : 'input-group-addon',
13563                     html : this.before
13564                 });
13565             }
13566             
13567             inputblock.cn.push(input);
13568             
13569             if(this.hasFeedback){
13570                 inputblock.cls += ' has-feedback';
13571                 inputblock.cn.push(feedback);
13572             }
13573             
13574             if (this.after) {
13575                 inputblock.cn.push({
13576                     tag :'span',
13577                     cls : 'input-group-addon',
13578                     html : this.after
13579                 });
13580             }
13581             
13582         }
13583         
13584         
13585         cfg = this.getAutoCreateLabel( cfg, inputblock );
13586
13587          
13588         
13589         if (this.disabled) {
13590             input.disabled=true;
13591         }
13592         
13593         return cfg;
13594         
13595     },
13596     /**
13597      * return the real textarea element.
13598      */
13599     inputEl: function ()
13600     {
13601         return this.el.select('textarea.form-control',true).first();
13602     },
13603     
13604     /**
13605      * Clear any invalid styles/messages for this field
13606      */
13607     clearInvalid : function()
13608     {
13609         
13610         if(!this.el || this.preventMark){ // not rendered
13611             return;
13612         }
13613         
13614         var label = this.el.select('label', true).first();
13615         //var icon = this.el.select('i.fa-star', true).first();
13616         
13617         //if(label && icon){
13618         //    icon.remove();
13619         //}
13620         this.el.removeClass( this.validClass);
13621         this.inputEl().removeClass('is-invalid');
13622          
13623         if(this.hasFeedback && this.inputType != 'hidden'){
13624             
13625             var feedback = this.el.select('.form-control-feedback', true).first();
13626             
13627             if(feedback){
13628                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13629
13630                 feedback.update('');
13631                 feedback.hide();
13632             }
13633             
13634         }
13635         
13636         this.fireEvent('valid', this);
13637     },
13638     
13639      /**
13640      * Mark this field as valid
13641      */
13642     markValid : function()
13643     {
13644         if(!this.el  || this.preventMark){ // not rendered
13645             return;
13646         }
13647         
13648         this.el.removeClass([this.invalidClass, this.validClass]);
13649         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13650         
13651         var feedback = this.el.select('.form-control-feedback', true).first();
13652             
13653         if(feedback){
13654             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13655             feedback.update('');
13656             feedback.hide();
13657         }
13658
13659         if(this.disabled || this.allowBlank){
13660             return;
13661         }
13662         
13663         var label = this.el.select('label', true).first();
13664         var icon = this.el.select('i.fa-star', true).first();
13665         
13666         //if(label && icon){
13667         //    icon.remove();
13668         //}
13669         if (Roo.bootstrap.version == 3) {
13670             this.el.addClass(this.validClass);
13671         } else {
13672             this.inputEl().addClass('is-valid');
13673         }
13674         
13675         
13676         if(this.hasFeedback && this.inputType != 'hidden'){
13677             
13678             var feedback = this.el.select('.form-control-feedback', true).first();
13679             
13680             if(feedback){
13681                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13682                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13683             }
13684             
13685         }
13686         
13687         this.fireEvent('valid', this);
13688     },
13689     
13690      /**
13691      * Mark this field as invalid
13692      * @param {String} msg The validation message
13693      */
13694     markInvalid : function(msg)
13695     {
13696         if(!this.el  || this.preventMark){ // not rendered
13697             return;
13698         }
13699         
13700         this.el.removeClass([this.invalidClass, this.validClass]);
13701         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13702         
13703         var feedback = this.el.select('.form-control-feedback', true).first();
13704             
13705         if(feedback){
13706             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13707             feedback.update('');
13708             feedback.hide();
13709         }
13710
13711         if(this.disabled){
13712             return;
13713         }
13714         
13715         var label = this.el.select('label', true).first();
13716         //var icon = this.el.select('i.fa-star', true).first();
13717         
13718         //if(!this.getValue().length && label && !icon){
13719           /*  this.el.createChild({
13720                 tag : 'i',
13721                 cls : 'text-danger fa fa-lg fa-star',
13722                 tooltip : 'This field is required',
13723                 style : 'margin-right:5px;'
13724             }, label, true);
13725             */
13726         //}
13727         
13728         if (Roo.bootstrap.version == 3) {
13729             this.el.addClass(this.invalidClass);
13730         } else {
13731             this.inputEl().addClass('is-invalid');
13732         }
13733         
13734         // fixme ... this may be depricated need to test..
13735         if(this.hasFeedback && this.inputType != 'hidden'){
13736             
13737             var feedback = this.el.select('.form-control-feedback', true).first();
13738             
13739             if(feedback){
13740                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13741                 
13742                 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13743
13744                 feedback.update(this.invalidText);
13745
13746                 if(!this.allowBlank && !this.getRawValue().length){
13747                     feedback.update(this.blankText);
13748                 }
13749
13750                 if(feedback.dom.innerHTML) {
13751                     feedback.show();
13752                 }
13753                 
13754             }
13755             
13756         }
13757         
13758         this.fireEvent('invalid', this, msg);
13759     }
13760 });
13761
13762  
13763 /*
13764  * - LGPL
13765  *
13766  * trigger field - base class for combo..
13767  * 
13768  */
13769  
13770 /**
13771  * @class Roo.bootstrap.form.TriggerField
13772  * @extends Roo.bootstrap.form.Input
13773  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13774  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13775  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13776  * for which you can provide a custom implementation.  For example:
13777  * <pre><code>
13778 var trigger = new Roo.bootstrap.form.TriggerField();
13779 trigger.onTriggerClick = myTriggerFn;
13780 trigger.applyTo('my-field');
13781 </code></pre>
13782  *
13783  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13784  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13785  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13786  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13787  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13788
13789  * @constructor
13790  * Create a new TriggerField.
13791  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13792  * to the base TextField)
13793  */
13794 Roo.bootstrap.form.TriggerField = function(config){
13795     this.mimicing = false;
13796     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13797 };
13798
13799 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13800     /**
13801      * @cfg {String} triggerClass A CSS class to apply to the trigger
13802      */
13803      /**
13804      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13805      */
13806     hideTrigger:false,
13807
13808     /**
13809      * @cfg {Boolean} removable (true|false) special filter default false
13810      */
13811     removable : false,
13812     
13813     /** @cfg {Boolean} grow @hide */
13814     /** @cfg {Number} growMin @hide */
13815     /** @cfg {Number} growMax @hide */
13816
13817     /**
13818      * @hide 
13819      * @method
13820      */
13821     autoSize: Roo.emptyFn,
13822     // private
13823     monitorTab : true,
13824     // private
13825     deferHeight : true,
13826
13827     
13828     actionMode : 'wrap',
13829     
13830     caret : false,
13831     
13832     
13833     getAutoCreate : function(){
13834        
13835         var align = this.labelAlign || this.parentLabelAlign();
13836         
13837         var id = Roo.id();
13838         
13839         var cfg = {
13840             cls: 'form-group' //input-group
13841         };
13842         
13843         
13844         var input =  {
13845             tag: 'input',
13846             id : id,
13847             type : this.inputType,
13848             cls : 'form-control',
13849             autocomplete: 'new-password',
13850             placeholder : this.placeholder || '' 
13851             
13852         };
13853         if (this.name) {
13854             input.name = this.name;
13855         }
13856         if (this.size) {
13857             input.cls += ' input-' + this.size;
13858         }
13859         
13860         if (this.disabled) {
13861             input.disabled=true;
13862         }
13863         
13864         var inputblock = input;
13865         
13866         if(this.hasFeedback && !this.allowBlank){
13867             
13868             var feedback = {
13869                 tag: 'span',
13870                 cls: 'glyphicon form-control-feedback'
13871             };
13872             
13873             if(this.removable && !this.editable  ){
13874                 inputblock = {
13875                     cls : 'has-feedback',
13876                     cn :  [
13877                         inputblock,
13878                         {
13879                             tag: 'button',
13880                             html : 'x',
13881                             cls : 'roo-combo-removable-btn close'
13882                         },
13883                         feedback
13884                     ] 
13885                 };
13886             } else {
13887                 inputblock = {
13888                     cls : 'has-feedback',
13889                     cn :  [
13890                         inputblock,
13891                         feedback
13892                     ] 
13893                 };
13894             }
13895
13896         } else {
13897             if(this.removable && !this.editable ){
13898                 inputblock = {
13899                     cls : 'roo-removable',
13900                     cn :  [
13901                         inputblock,
13902                         {
13903                             tag: 'button',
13904                             html : 'x',
13905                             cls : 'roo-combo-removable-btn close'
13906                         }
13907                     ] 
13908                 };
13909             }
13910         }
13911         
13912         if (this.before || this.after) {
13913             
13914             inputblock = {
13915                 cls : 'input-group',
13916                 cn :  [] 
13917             };
13918             if (this.before) {
13919                 inputblock.cn.push({
13920                     tag :'span',
13921                     cls : 'input-group-addon input-group-prepend input-group-text',
13922                     html : this.before
13923                 });
13924             }
13925             
13926             inputblock.cn.push(input);
13927             
13928             if(this.hasFeedback && !this.allowBlank){
13929                 inputblock.cls += ' has-feedback';
13930                 inputblock.cn.push(feedback);
13931             }
13932             
13933             if (this.after) {
13934                 inputblock.cn.push({
13935                     tag :'span',
13936                     cls : 'input-group-addon input-group-append input-group-text',
13937                     html : this.after
13938                 });
13939             }
13940             
13941         };
13942         
13943       
13944         
13945         var ibwrap = inputblock;
13946         
13947         if(this.multiple){
13948             ibwrap = {
13949                 tag: 'ul',
13950                 cls: 'roo-select2-choices',
13951                 cn:[
13952                     {
13953                         tag: 'li',
13954                         cls: 'roo-select2-search-field',
13955                         cn: [
13956
13957                             inputblock
13958                         ]
13959                     }
13960                 ]
13961             };
13962                 
13963         }
13964         
13965         var combobox = {
13966             cls: 'roo-select2-container input-group',
13967             cn: [
13968                  {
13969                     tag: 'input',
13970                     type : 'hidden',
13971                     cls: 'form-hidden-field'
13972                 },
13973                 ibwrap
13974             ]
13975         };
13976         
13977         if(!this.multiple && this.showToggleBtn){
13978             
13979             var caret = {
13980                         tag: 'span',
13981                         cls: 'caret'
13982              };
13983             if (this.caret != false) {
13984                 caret = {
13985                      tag: 'i',
13986                      cls: 'fa fa-' + this.caret
13987                 };
13988                 
13989             }
13990             
13991             combobox.cn.push({
13992                 tag :'span',
13993                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13994                 cn : [
13995                     Roo.bootstrap.version == 3 ? caret : '',
13996                     {
13997                         tag: 'span',
13998                         cls: 'combobox-clear',
13999                         cn  : [
14000                             {
14001                                 tag : 'i',
14002                                 cls: 'icon-remove'
14003                             }
14004                         ]
14005                     }
14006                 ]
14007
14008             })
14009         }
14010         
14011         if(this.multiple){
14012             combobox.cls += ' roo-select2-container-multi';
14013         }
14014          var indicator = {
14015             tag : 'i',
14016             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14017             tooltip : 'This field is required'
14018         };
14019       
14020         if (this.allowBlank) {
14021             indicator = {
14022                 tag : 'i',
14023                 style : 'display:none'
14024             };
14025         }
14026          
14027         
14028         
14029         if (align ==='left' && this.fieldLabel.length) {
14030             
14031             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14032
14033             cfg.cn = [
14034                 indicator,
14035                 {
14036                     tag: 'label',
14037                     'for' :  id,
14038                     cls : 'control-label',
14039                     html : this.fieldLabel
14040
14041                 },
14042                 {
14043                     cls : "", 
14044                     cn: [
14045                         combobox
14046                     ]
14047                 }
14048
14049             ];
14050             
14051             var labelCfg = cfg.cn[1];
14052             var contentCfg = cfg.cn[2];
14053             
14054             if(this.indicatorpos == 'right'){
14055                 cfg.cn = [
14056                     {
14057                         tag: 'label',
14058                         'for' :  id,
14059                         cls : 'control-label',
14060                         cn : [
14061                             {
14062                                 tag : 'span',
14063                                 html : this.fieldLabel
14064                             },
14065                             indicator
14066                         ]
14067                     },
14068                     {
14069                         cls : "", 
14070                         cn: [
14071                             combobox
14072                         ]
14073                     }
14074
14075                 ];
14076                 
14077                 labelCfg = cfg.cn[0];
14078                 contentCfg = cfg.cn[1];
14079             }
14080             
14081             if(this.labelWidth > 12){
14082                 labelCfg.style = "width: " + this.labelWidth + 'px';
14083             }
14084             
14085             if(this.labelWidth < 13 && this.labelmd == 0){
14086                 this.labelmd = this.labelWidth;
14087             }
14088             
14089             if(this.labellg > 0){
14090                 labelCfg.cls += ' col-lg-' + this.labellg;
14091                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14092             }
14093             
14094             if(this.labelmd > 0){
14095                 labelCfg.cls += ' col-md-' + this.labelmd;
14096                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14097             }
14098             
14099             if(this.labelsm > 0){
14100                 labelCfg.cls += ' col-sm-' + this.labelsm;
14101                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14102             }
14103             
14104             if(this.labelxs > 0){
14105                 labelCfg.cls += ' col-xs-' + this.labelxs;
14106                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14107             }
14108             
14109         } else if ( this.fieldLabel.length) {
14110 //                Roo.log(" label");
14111             cfg.cn = [
14112                 indicator,
14113                {
14114                    tag: 'label',
14115                    //cls : 'input-group-addon',
14116                    html : this.fieldLabel
14117
14118                },
14119
14120                combobox
14121
14122             ];
14123             
14124             if(this.indicatorpos == 'right'){
14125                 
14126                 cfg.cn = [
14127                     {
14128                        tag: 'label',
14129                        cn : [
14130                            {
14131                                tag : 'span',
14132                                html : this.fieldLabel
14133                            },
14134                            indicator
14135                        ]
14136
14137                     },
14138                     combobox
14139
14140                 ];
14141
14142             }
14143
14144         } else {
14145             
14146 //                Roo.log(" no label && no align");
14147                 cfg = combobox
14148                      
14149                 
14150         }
14151         
14152         var settings=this;
14153         ['xs','sm','md','lg'].map(function(size){
14154             if (settings[size]) {
14155                 cfg.cls += ' col-' + size + '-' + settings[size];
14156             }
14157         });
14158         
14159         return cfg;
14160         
14161     },
14162     
14163     
14164     
14165     // private
14166     onResize : function(w, h){
14167 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14168 //        if(typeof w == 'number'){
14169 //            var x = w - this.trigger.getWidth();
14170 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14171 //            this.trigger.setStyle('left', x+'px');
14172 //        }
14173     },
14174
14175     // private
14176     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14177
14178     // private
14179     getResizeEl : function(){
14180         return this.inputEl();
14181     },
14182
14183     // private
14184     getPositionEl : function(){
14185         return this.inputEl();
14186     },
14187
14188     // private
14189     alignErrorIcon : function(){
14190         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14191     },
14192
14193     // private
14194     initEvents : function(){
14195         
14196         this.createList();
14197         
14198         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14199         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14200         if(!this.multiple && this.showToggleBtn){
14201             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14202             if(this.hideTrigger){
14203                 this.trigger.setDisplayed(false);
14204             }
14205             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14206         }
14207         
14208         if(this.multiple){
14209             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14210         }
14211         
14212         if(this.removable && !this.editable && !this.tickable){
14213             var close = this.closeTriggerEl();
14214             
14215             if(close){
14216                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14217                 close.on('click', this.removeBtnClick, this, close);
14218             }
14219         }
14220         
14221         //this.trigger.addClassOnOver('x-form-trigger-over');
14222         //this.trigger.addClassOnClick('x-form-trigger-click');
14223         
14224         //if(!this.width){
14225         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14226         //}
14227     },
14228     
14229     closeTriggerEl : function()
14230     {
14231         var close = this.el.select('.roo-combo-removable-btn', true).first();
14232         return close ? close : false;
14233     },
14234     
14235     removeBtnClick : function(e, h, el)
14236     {
14237         e.preventDefault();
14238         
14239         if(this.fireEvent("remove", this) !== false){
14240             this.reset();
14241             this.fireEvent("afterremove", this)
14242         }
14243     },
14244     
14245     createList : function()
14246     {
14247         this.list = Roo.get(document.body).createChild({
14248             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14249             cls: 'typeahead typeahead-long dropdown-menu shadow',
14250             style: 'display:none'
14251         });
14252         
14253         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14254         
14255     },
14256
14257     // private
14258     initTrigger : function(){
14259        
14260     },
14261
14262     // private
14263     onDestroy : function(){
14264         if(this.trigger){
14265             this.trigger.removeAllListeners();
14266           //  this.trigger.remove();
14267         }
14268         //if(this.wrap){
14269         //    this.wrap.remove();
14270         //}
14271         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14272     },
14273
14274     // private
14275     onFocus : function(){
14276         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14277         /*
14278         if(!this.mimicing){
14279             this.wrap.addClass('x-trigger-wrap-focus');
14280             this.mimicing = true;
14281             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14282             if(this.monitorTab){
14283                 this.el.on("keydown", this.checkTab, this);
14284             }
14285         }
14286         */
14287     },
14288
14289     // private
14290     checkTab : function(e){
14291         if(e.getKey() == e.TAB){
14292             this.triggerBlur();
14293         }
14294     },
14295
14296     // private
14297     onBlur : function(){
14298         // do nothing
14299     },
14300
14301     // private
14302     mimicBlur : function(e, t){
14303         /*
14304         if(!this.wrap.contains(t) && this.validateBlur()){
14305             this.triggerBlur();
14306         }
14307         */
14308     },
14309
14310     // private
14311     triggerBlur : function(){
14312         this.mimicing = false;
14313         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14314         if(this.monitorTab){
14315             this.el.un("keydown", this.checkTab, this);
14316         }
14317         //this.wrap.removeClass('x-trigger-wrap-focus');
14318         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14319     },
14320
14321     // private
14322     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14323     validateBlur : function(e, t){
14324         return true;
14325     },
14326
14327     // private
14328     onDisable : function(){
14329         this.inputEl().dom.disabled = true;
14330         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14331         //if(this.wrap){
14332         //    this.wrap.addClass('x-item-disabled');
14333         //}
14334     },
14335
14336     // private
14337     onEnable : function(){
14338         this.inputEl().dom.disabled = false;
14339         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14340         //if(this.wrap){
14341         //    this.el.removeClass('x-item-disabled');
14342         //}
14343     },
14344
14345     // private
14346     onShow : function(){
14347         var ae = this.getActionEl();
14348         
14349         if(ae){
14350             ae.dom.style.display = '';
14351             ae.dom.style.visibility = 'visible';
14352         }
14353     },
14354
14355     // private
14356     
14357     onHide : function(){
14358         var ae = this.getActionEl();
14359         ae.dom.style.display = 'none';
14360     },
14361
14362     /**
14363      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14364      * by an implementing function.
14365      * @method
14366      * @param {EventObject} e
14367      */
14368     onTriggerClick : Roo.emptyFn
14369 });
14370  
14371 /*
14372 * Licence: LGPL
14373 */
14374
14375 /**
14376  * @class Roo.bootstrap.form.CardUploader
14377  * @extends Roo.bootstrap.Button
14378  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14379  * @cfg {Number} errorTimeout default 3000
14380  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14381  * @cfg {Array}  html The button text.
14382
14383  *
14384  * @constructor
14385  * Create a new CardUploader
14386  * @param {Object} config The config object
14387  */
14388
14389 Roo.bootstrap.form.CardUploader = function(config){
14390     
14391  
14392     
14393     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14394     
14395     
14396     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14397         return r.data.id
14398      });
14399     
14400      this.addEvents({
14401          // raw events
14402         /**
14403          * @event preview
14404          * When a image is clicked on - and needs to display a slideshow or similar..
14405          * @param {Roo.bootstrap.Card} this
14406          * @param {Object} The image information data 
14407          *
14408          */
14409         'preview' : true,
14410          /**
14411          * @event download
14412          * When a the download link is clicked
14413          * @param {Roo.bootstrap.Card} this
14414          * @param {Object} The image information data  contains 
14415          */
14416         'download' : true
14417         
14418     });
14419 };
14420  
14421 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14422     
14423      
14424     errorTimeout : 3000,
14425      
14426     images : false,
14427    
14428     fileCollection : false,
14429     allowBlank : true,
14430     
14431     getAutoCreate : function()
14432     {
14433         
14434         var cfg =  {
14435             cls :'form-group' ,
14436             cn : [
14437                
14438                 {
14439                     tag: 'label',
14440                    //cls : 'input-group-addon',
14441                     html : this.fieldLabel
14442
14443                 },
14444
14445                 {
14446                     tag: 'input',
14447                     type : 'hidden',
14448                     name : this.name,
14449                     value : this.value,
14450                     cls : 'd-none  form-control'
14451                 },
14452                 
14453                 {
14454                     tag: 'input',
14455                     multiple : 'multiple',
14456                     type : 'file',
14457                     cls : 'd-none  roo-card-upload-selector'
14458                 },
14459                 
14460                 {
14461                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14462                 },
14463                 {
14464                     cls : 'card-columns roo-card-uploader-container'
14465                 }
14466
14467             ]
14468         };
14469            
14470          
14471         return cfg;
14472     },
14473     
14474     getChildContainer : function() /// what children are added to.
14475     {
14476         return this.containerEl;
14477     },
14478    
14479     getButtonContainer : function() /// what children are added to.
14480     {
14481         return this.el.select(".roo-card-uploader-button-container").first();
14482     },
14483    
14484     initEvents : function()
14485     {
14486         
14487         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14488         
14489         var t = this;
14490         this.addxtype({
14491             xns: Roo.bootstrap,
14492
14493             xtype : 'Button',
14494             container_method : 'getButtonContainer' ,            
14495             html :  this.html, // fix changable?
14496             cls : 'w-100 ',
14497             listeners : {
14498                 'click' : function(btn, e) {
14499                     t.onClick(e);
14500                 }
14501             }
14502         });
14503         
14504         
14505         
14506         
14507         this.urlAPI = (window.createObjectURL && window) || 
14508                                 (window.URL && URL.revokeObjectURL && URL) || 
14509                                 (window.webkitURL && webkitURL);
14510                         
14511          
14512          
14513          
14514         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14515         
14516         this.selectorEl.on('change', this.onFileSelected, this);
14517         if (this.images) {
14518             var t = this;
14519             this.images.forEach(function(img) {
14520                 t.addCard(img)
14521             });
14522             this.images = false;
14523         }
14524         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14525          
14526        
14527     },
14528     
14529    
14530     onClick : function(e)
14531     {
14532         e.preventDefault();
14533          
14534         this.selectorEl.dom.click();
14535          
14536     },
14537     
14538     onFileSelected : function(e)
14539     {
14540         e.preventDefault();
14541         
14542         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14543             return;
14544         }
14545         
14546         Roo.each(this.selectorEl.dom.files, function(file){    
14547             this.addFile(file);
14548         }, this);
14549          
14550     },
14551     
14552       
14553     
14554       
14555     
14556     addFile : function(file)
14557     {
14558            
14559         if(typeof(file) === 'string'){
14560             throw "Add file by name?"; // should not happen
14561             return;
14562         }
14563         
14564         if(!file || !this.urlAPI){
14565             return;
14566         }
14567         
14568         // file;
14569         // file.type;
14570         
14571         var _this = this;
14572         
14573         
14574         var url = _this.urlAPI.createObjectURL( file);
14575            
14576         this.addCard({
14577             id : Roo.bootstrap.form.CardUploader.ID--,
14578             is_uploaded : false,
14579             src : url,
14580             srcfile : file,
14581             title : file.name,
14582             mimetype : file.type,
14583             preview : false,
14584             is_deleted : 0
14585         });
14586         
14587     },
14588     
14589     /**
14590      * addCard - add an Attachment to the uploader
14591      * @param data - the data about the image to upload
14592      *
14593      * {
14594           id : 123
14595           title : "Title of file",
14596           is_uploaded : false,
14597           src : "http://.....",
14598           srcfile : { the File upload object },
14599           mimetype : file.type,
14600           preview : false,
14601           is_deleted : 0
14602           .. any other data...
14603         }
14604      *
14605      * 
14606     */
14607     
14608     addCard : function (data)
14609     {
14610         // hidden input element?
14611         // if the file is not an image...
14612         //then we need to use something other that and header_image
14613         var t = this;
14614         //   remove.....
14615         var footer = [
14616             {
14617                 xns : Roo.bootstrap,
14618                 xtype : 'CardFooter',
14619                  items: [
14620                     {
14621                         xns : Roo.bootstrap,
14622                         xtype : 'Element',
14623                         cls : 'd-flex',
14624                         items : [
14625                             
14626                             {
14627                                 xns : Roo.bootstrap,
14628                                 xtype : 'Button',
14629                                 html : String.format("<small>{0}</small>", data.title),
14630                                 cls : 'col-10 text-left',
14631                                 size: 'sm',
14632                                 weight: 'link',
14633                                 fa : 'download',
14634                                 listeners : {
14635                                     click : function() {
14636                                      
14637                                         t.fireEvent( "download", t, data );
14638                                     }
14639                                 }
14640                             },
14641                           
14642                             {
14643                                 xns : Roo.bootstrap,
14644                                 xtype : 'Button',
14645                                 style: 'max-height: 28px; ',
14646                                 size : 'sm',
14647                                 weight: 'danger',
14648                                 cls : 'col-2',
14649                                 fa : 'times',
14650                                 listeners : {
14651                                     click : function() {
14652                                         t.removeCard(data.id)
14653                                     }
14654                                 }
14655                             }
14656                         ]
14657                     }
14658                     
14659                 ] 
14660             }
14661             
14662         ];
14663         
14664         var cn = this.addxtype(
14665             {
14666                  
14667                 xns : Roo.bootstrap,
14668                 xtype : 'Card',
14669                 closeable : true,
14670                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14671                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14672                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14673                 data : data,
14674                 html : false,
14675                  
14676                 items : footer,
14677                 initEvents : function() {
14678                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14679                     var card = this;
14680                     this.imgEl = this.el.select('.card-img-top').first();
14681                     if (this.imgEl) {
14682                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14683                         this.imgEl.set({ 'pointer' : 'cursor' });
14684                                   
14685                     }
14686                     this.getCardFooter().addClass('p-1');
14687                     
14688                   
14689                 }
14690                 
14691             }
14692         );
14693         // dont' really need ot update items.
14694         // this.items.push(cn);
14695         this.fileCollection.add(cn);
14696         
14697         if (!data.srcfile) {
14698             this.updateInput();
14699             return;
14700         }
14701             
14702         var _t = this;
14703         var reader = new FileReader();
14704         reader.addEventListener("load", function() {  
14705             data.srcdata =  reader.result;
14706             _t.updateInput();
14707         });
14708         reader.readAsDataURL(data.srcfile);
14709         
14710         
14711         
14712     },
14713     removeCard : function(id)
14714     {
14715         
14716         var card  = this.fileCollection.get(id);
14717         card.data.is_deleted = 1;
14718         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14719         //this.fileCollection.remove(card);
14720         //this.items = this.items.filter(function(e) { return e != card });
14721         // dont' really need ot update items.
14722         card.el.dom.parentNode.removeChild(card.el.dom);
14723         this.updateInput();
14724
14725         
14726     },
14727     reset: function()
14728     {
14729         this.fileCollection.each(function(card) {
14730             if (card.el.dom && card.el.dom.parentNode) {
14731                 card.el.dom.parentNode.removeChild(card.el.dom);
14732             }
14733         });
14734         this.fileCollection.clear();
14735         this.updateInput();
14736     },
14737     
14738     updateInput : function()
14739     {
14740          var data = [];
14741         this.fileCollection.each(function(e) {
14742             data.push(e.data);
14743             
14744         });
14745         this.inputEl().dom.value = JSON.stringify(data);
14746         
14747         
14748         
14749     }
14750     
14751     
14752 });
14753
14754
14755 Roo.bootstrap.form.CardUploader.ID = -1;/**
14756  * 
14757  * @class Roo.bootstrap.form.MultiLineTag
14758  * @param {Object} config The config object
14759  * 
14760  */
14761
14762 Roo.bootstrap.form.MultiLineTag = function(config){
14763     Roo.bootstrap.form.MultiLineTag.superclass.constructor.call(this, config);
14764
14765     this.addEvents({
14766         /**
14767          * @event beforeload
14768          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14769          * the load action will be canceled.
14770          * @param {Roo.boostrap.form.MultiLineTag} this
14771          * @param {Store} store
14772          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14773          */
14774          beforeload : true
14775     });
14776 };
14777
14778 Roo.extend(Roo.bootstrap.form.MultiLineTag, Roo.bootstrap.form.Input,  {
14779     tagRows : [],
14780     minimumRow : 2,
14781
14782     // for combo box
14783     displayField : '',
14784     valueField : '',
14785     placeholder : '',
14786     queryParam : '',
14787     listWidth : 300,
14788     minChars : 2,
14789
14790     // for combo box store
14791     url : undefined,
14792     fields : [],
14793
14794
14795
14796     getAutoCreate : function()
14797     {
14798         var config = {
14799             cls : 'roo-multi-line-tag form-group'
14800         };
14801
14802         config = this.getAutoCreateLabel( config, {
14803             cls : 'roo-multi-line-tag-container'
14804         } );
14805
14806         return config;
14807     },
14808
14809     initEvents : function()
14810     {
14811         this.tagRows = [];
14812
14813         for (var i = 0; i < this.minimumRow; i++) {
14814             this.addTagRow();
14815         }
14816     },
14817
14818     addTagRow : function()
14819     {
14820         var _this = this; 
14821
14822         var comboBox = Roo.factory({
14823             xns: Roo.bootstrap.form,
14824             xtype : 'ComboBox',
14825             editable : true,
14826             triggerAction: 'all',
14827             minChars: _this.minChars,
14828             displayField: _this.displayField,
14829             valueField : _this.valueField,
14830             listWidth: _this.listWidth,
14831             placeholder : _this.placeholder,
14832             queryParam : _this.queryParam,
14833             store : {
14834                 xns : Roo.data,
14835                 xtype : 'Store',
14836                 listeners : {
14837                     beforeload : function(_self, options)
14838                     {
14839                         _this.fireEvent('beforeload', _this, _self, options);
14840                     }
14841                 },
14842                 proxy : {
14843                     xns : Roo.data,
14844                     xtype : 'HttpProxy',
14845                     method : 'GET',
14846                     url : _this.url
14847                 },
14848                 reader : {
14849                     xns : Roo.data,
14850                     xtype : 'JsonReader',
14851                     fields : _this.fields
14852                 }
14853             },
14854             listeners : {
14855                 'render' : function (_self) {
14856                     _self.inputEl().on('keyup', function(e) {
14857                         if(_this.shouldAutoAddTagRow()) {
14858                             _this.addTagRow();
14859                         }
14860                     });
14861                     _self.inputEl().on('change', function(e) {
14862                         _this.fireEvent('change', _this, _this.getValue(), false);
14863                         _this.showHideRemoveBtn();
14864
14865                     });
14866                 },
14867                 'select' : function(_self, record, index) {
14868                     _this.fireEvent('change', _this, _this.getValue(), false);
14869                 }
14870             }
14871         });
14872
14873         var button = Roo.factory({
14874             xns : Roo.bootstrap,
14875             xtype : 'Button',
14876             html : '-'
14877         });
14878
14879         var row = {
14880             xns : Roo.bootstrap,
14881             xtype : 'Row',
14882             items : [
14883                 comboBox,
14884                 button
14885             ],
14886             listeners : {
14887                 'render' : function (_self) {
14888                     this.inputCb = comboBox;
14889                     this.removeBtn = button;
14890
14891                     this.removeBtn.on('click', function() {
14892                         _this.removeTagRow(_self);
14893                         _this.fireEvent('change', _this, _this.getValue(), false);
14894                     });
14895                 }
14896             }
14897         };
14898         this.tagRows.push(this.addxtype(row));
14899
14900         _this.showHideRemoveBtn();
14901     },
14902
14903     // a new tags should be added automatically when all existing tags are not empty
14904     shouldAutoAddTagRow : function()
14905     {
14906         var ret = true;
14907
14908         Roo.each(this.tagRows, function(r) {
14909             if(r.inputCb.getRawValue() == '') {
14910                 ret = false;
14911             }
14912         });
14913
14914         return ret;
14915     },
14916
14917     removeTagRow : function(row)
14918     {
14919         row.destroy();
14920         this.tagRows.splice(this.tagRows.indexOf(row), 1);
14921         this.showHideRemoveBtn();
14922     },
14923
14924     // hide all remove buttons if there are {minimumRow} or less tags
14925     // hide the remove button for empty tag
14926     showHideRemoveBtn : function()
14927     {
14928         var _this = this;
14929         
14930         Roo.each(this.tagRows, function (r) {
14931
14932             r.removeBtn.show();
14933
14934             if(_this.tagRows.length <= _this.minimumRow || r.inputCb.getRawValue() == '') {
14935                 r.removeBtn.hide();
14936             }
14937         });
14938     },
14939
14940     getValue : function()
14941     {
14942         var _this = this;
14943         var tags = [];
14944         Roo.each(_this.tagRows, function(r) {
14945             var value = r.inputCb.getRawValue();
14946             if(value != '') {
14947                 var tag = {};
14948                 tag[_this.valueField] = r.inputCb.getRawValue();
14949                 tags.push(tag);
14950             }
14951         });
14952         
14953         return JSON.stringify(tags);
14954     },
14955
14956     setValue : function(json)
14957     {
14958
14959         // remove all old tags
14960         var oldTotal = this.tagRows.length;
14961
14962         for(var i = 0; i < oldTotal; i ++) {
14963             this.removeTagRow(this.tagRows[0]);
14964         }
14965
14966         // empty tag if invalid json
14967         var arr = [];
14968
14969         try {
14970             // set new tags
14971             arr = JSON.parse(json);
14972         }
14973         catch {}
14974
14975         for (var i = 0; i < arr.length; i ++) {
14976             this.addTagRow();
14977             this.tagRows[i].inputCb.setRawValue(arr[i][this.valueField]);
14978         }
14979
14980         // always add one extra empty tag
14981         this.addTagRow();
14982
14983         // add empty tags until there are {minimumRow} tags
14984         while(this.tagRows.length < this.minimumRow) {
14985             this.addTagRow();
14986         }
14987         
14988     },
14989
14990     getChildContainer : function()
14991     {
14992         return Roo.select('.roo-multi-line-tag-container', true).elements[0];
14993     }
14994 });/*
14995  * Based on:
14996  * Ext JS Library 1.1.1
14997  * Copyright(c) 2006-2007, Ext JS, LLC.
14998  *
14999  * Originally Released Under LGPL - original licence link has changed is not relivant.
15000  *
15001  * Fork - LGPL
15002  * <script type="text/javascript">
15003  */
15004
15005
15006 /**
15007  * @class Roo.data.SortTypes
15008  * @static
15009  * Defines the default sorting (casting?) comparison functions used when sorting data.
15010  */
15011 Roo.data.SortTypes = {
15012     /**
15013      * Default sort that does nothing
15014      * @param {Mixed} s The value being converted
15015      * @return {Mixed} The comparison value
15016      */
15017     none : function(s){
15018         return s;
15019     },
15020     
15021     /**
15022      * The regular expression used to strip tags
15023      * @type {RegExp}
15024      * @property
15025      */
15026     stripTagsRE : /<\/?[^>]+>/gi,
15027     
15028     /**
15029      * Strips all HTML tags to sort on text only
15030      * @param {Mixed} s The value being converted
15031      * @return {String} The comparison value
15032      */
15033     asText : function(s){
15034         return String(s).replace(this.stripTagsRE, "");
15035     },
15036     
15037     /**
15038      * Strips all HTML tags to sort on text only - Case insensitive
15039      * @param {Mixed} s The value being converted
15040      * @return {String} The comparison value
15041      */
15042     asUCText : function(s){
15043         return String(s).toUpperCase().replace(this.stripTagsRE, "");
15044     },
15045     
15046     /**
15047      * Case insensitive string
15048      * @param {Mixed} s The value being converted
15049      * @return {String} The comparison value
15050      */
15051     asUCString : function(s) {
15052         return String(s).toUpperCase();
15053     },
15054     
15055     /**
15056      * Date sorting
15057      * @param {Mixed} s The value being converted
15058      * @return {Number} The comparison value
15059      */
15060     asDate : function(s) {
15061         if(!s){
15062             return 0;
15063         }
15064         if(s instanceof Date){
15065             return s.getTime();
15066         }
15067         return Date.parse(String(s));
15068     },
15069     
15070     /**
15071      * Float sorting
15072      * @param {Mixed} s The value being converted
15073      * @return {Float} The comparison value
15074      */
15075     asFloat : function(s) {
15076         var val = parseFloat(String(s).replace(/,/g, ""));
15077         if(isNaN(val)) {
15078             val = 0;
15079         }
15080         return val;
15081     },
15082     
15083     /**
15084      * Integer sorting
15085      * @param {Mixed} s The value being converted
15086      * @return {Number} The comparison value
15087      */
15088     asInt : function(s) {
15089         var val = parseInt(String(s).replace(/,/g, ""));
15090         if(isNaN(val)) {
15091             val = 0;
15092         }
15093         return val;
15094     }
15095 };/*
15096  * Based on:
15097  * Ext JS Library 1.1.1
15098  * Copyright(c) 2006-2007, Ext JS, LLC.
15099  *
15100  * Originally Released Under LGPL - original licence link has changed is not relivant.
15101  *
15102  * Fork - LGPL
15103  * <script type="text/javascript">
15104  */
15105
15106 /**
15107 * @class Roo.data.Record
15108  * Instances of this class encapsulate both record <em>definition</em> information, and record
15109  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
15110  * to access Records cached in an {@link Roo.data.Store} object.<br>
15111  * <p>
15112  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
15113  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
15114  * objects.<br>
15115  * <p>
15116  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
15117  * @constructor
15118  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
15119  * {@link #create}. The parameters are the same.
15120  * @param {Array} data An associative Array of data values keyed by the field name.
15121  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
15122  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
15123  * not specified an integer id is generated.
15124  */
15125 Roo.data.Record = function(data, id){
15126     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
15127     this.data = data;
15128 };
15129
15130 /**
15131  * Generate a constructor for a specific record layout.
15132  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
15133  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
15134  * Each field definition object may contain the following properties: <ul>
15135  * <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,
15136  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
15137  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
15138  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
15139  * is being used, then this is a string containing the javascript expression to reference the data relative to 
15140  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
15141  * to the data item relative to the record element. If the mapping expression is the same as the field name,
15142  * this may be omitted.</p></li>
15143  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
15144  * <ul><li>auto (Default, implies no conversion)</li>
15145  * <li>string</li>
15146  * <li>int</li>
15147  * <li>float</li>
15148  * <li>boolean</li>
15149  * <li>date</li></ul></p></li>
15150  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
15151  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
15152  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
15153  * by the Reader into an object that will be stored in the Record. It is passed the
15154  * following parameters:<ul>
15155  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
15156  * </ul></p></li>
15157  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
15158  * </ul>
15159  * <br>usage:<br><pre><code>
15160 var TopicRecord = Roo.data.Record.create(
15161     {name: 'title', mapping: 'topic_title'},
15162     {name: 'author', mapping: 'username'},
15163     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
15164     {name: 'lastPost', mapping: 'post_time', type: 'date'},
15165     {name: 'lastPoster', mapping: 'user2'},
15166     {name: 'excerpt', mapping: 'post_text'}
15167 );
15168
15169 var myNewRecord = new TopicRecord({
15170     title: 'Do my job please',
15171     author: 'noobie',
15172     totalPosts: 1,
15173     lastPost: new Date(),
15174     lastPoster: 'Animal',
15175     excerpt: 'No way dude!'
15176 });
15177 myStore.add(myNewRecord);
15178 </code></pre>
15179  * @method create
15180  * @static
15181  */
15182 Roo.data.Record.create = function(o){
15183     var f = function(){
15184         f.superclass.constructor.apply(this, arguments);
15185     };
15186     Roo.extend(f, Roo.data.Record);
15187     var p = f.prototype;
15188     p.fields = new Roo.util.MixedCollection(false, function(field){
15189         return field.name;
15190     });
15191     for(var i = 0, len = o.length; i < len; i++){
15192         p.fields.add(new Roo.data.Field(o[i]));
15193     }
15194     f.getField = function(name){
15195         return p.fields.get(name);  
15196     };
15197     return f;
15198 };
15199
15200 Roo.data.Record.AUTO_ID = 1000;
15201 Roo.data.Record.EDIT = 'edit';
15202 Roo.data.Record.REJECT = 'reject';
15203 Roo.data.Record.COMMIT = 'commit';
15204
15205 Roo.data.Record.prototype = {
15206     /**
15207      * Readonly flag - true if this record has been modified.
15208      * @type Boolean
15209      */
15210     dirty : false,
15211     editing : false,
15212     error: null,
15213     modified: null,
15214
15215     // private
15216     join : function(store){
15217         this.store = store;
15218     },
15219
15220     /**
15221      * Set the named field to the specified value.
15222      * @param {String} name The name of the field to set.
15223      * @param {Object} value The value to set the field to.
15224      */
15225     set : function(name, value){
15226         if(this.data[name] == value){
15227             return;
15228         }
15229         this.dirty = true;
15230         if(!this.modified){
15231             this.modified = {};
15232         }
15233         if(typeof this.modified[name] == 'undefined'){
15234             this.modified[name] = this.data[name];
15235         }
15236         this.data[name] = value;
15237         if(!this.editing && this.store){
15238             this.store.afterEdit(this);
15239         }       
15240     },
15241
15242     /**
15243      * Get the value of the named field.
15244      * @param {String} name The name of the field to get the value of.
15245      * @return {Object} The value of the field.
15246      */
15247     get : function(name){
15248         return this.data[name]; 
15249     },
15250
15251     // private
15252     beginEdit : function(){
15253         this.editing = true;
15254         this.modified = {}; 
15255     },
15256
15257     // private
15258     cancelEdit : function(){
15259         this.editing = false;
15260         delete this.modified;
15261     },
15262
15263     // private
15264     endEdit : function(){
15265         this.editing = false;
15266         if(this.dirty && this.store){
15267             this.store.afterEdit(this);
15268         }
15269     },
15270
15271     /**
15272      * Usually called by the {@link Roo.data.Store} which owns the Record.
15273      * Rejects all changes made to the Record since either creation, or the last commit operation.
15274      * Modified fields are reverted to their original values.
15275      * <p>
15276      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15277      * of reject operations.
15278      */
15279     reject : function(){
15280         var m = this.modified;
15281         for(var n in m){
15282             if(typeof m[n] != "function"){
15283                 this.data[n] = m[n];
15284             }
15285         }
15286         this.dirty = false;
15287         delete this.modified;
15288         this.editing = false;
15289         if(this.store){
15290             this.store.afterReject(this);
15291         }
15292     },
15293
15294     /**
15295      * Usually called by the {@link Roo.data.Store} which owns the Record.
15296      * Commits all changes made to the Record since either creation, or the last commit operation.
15297      * <p>
15298      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15299      * of commit operations.
15300      */
15301     commit : function(){
15302         this.dirty = false;
15303         delete this.modified;
15304         this.editing = false;
15305         if(this.store){
15306             this.store.afterCommit(this);
15307         }
15308     },
15309
15310     // private
15311     hasError : function(){
15312         return this.error != null;
15313     },
15314
15315     // private
15316     clearError : function(){
15317         this.error = null;
15318     },
15319
15320     /**
15321      * Creates a copy of this record.
15322      * @param {String} id (optional) A new record id if you don't want to use this record's id
15323      * @return {Record}
15324      */
15325     copy : function(newId) {
15326         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15327     }
15328 };/*
15329  * Based on:
15330  * Ext JS Library 1.1.1
15331  * Copyright(c) 2006-2007, Ext JS, LLC.
15332  *
15333  * Originally Released Under LGPL - original licence link has changed is not relivant.
15334  *
15335  * Fork - LGPL
15336  * <script type="text/javascript">
15337  */
15338
15339
15340
15341 /**
15342  * @class Roo.data.Store
15343  * @extends Roo.util.Observable
15344  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15345  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15346  * <p>
15347  * 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
15348  * has no knowledge of the format of the data returned by the Proxy.<br>
15349  * <p>
15350  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15351  * instances from the data object. These records are cached and made available through accessor functions.
15352  * @constructor
15353  * Creates a new Store.
15354  * @param {Object} config A config object containing the objects needed for the Store to access data,
15355  * and read the data into Records.
15356  */
15357 Roo.data.Store = function(config){
15358     this.data = new Roo.util.MixedCollection(false);
15359     this.data.getKey = function(o){
15360         return o.id;
15361     };
15362     this.baseParams = {};
15363     // private
15364     this.paramNames = {
15365         "start" : "start",
15366         "limit" : "limit",
15367         "sort" : "sort",
15368         "dir" : "dir",
15369         "multisort" : "_multisort"
15370     };
15371
15372     if(config && config.data){
15373         this.inlineData = config.data;
15374         delete config.data;
15375     }
15376
15377     Roo.apply(this, config);
15378     
15379     if(this.reader){ // reader passed
15380         this.reader = Roo.factory(this.reader, Roo.data);
15381         this.reader.xmodule = this.xmodule || false;
15382         if(!this.recordType){
15383             this.recordType = this.reader.recordType;
15384         }
15385         if(this.reader.onMetaChange){
15386             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15387         }
15388     }
15389
15390     if(this.recordType){
15391         this.fields = this.recordType.prototype.fields;
15392     }
15393     this.modified = [];
15394
15395     this.addEvents({
15396         /**
15397          * @event datachanged
15398          * Fires when the data cache has changed, and a widget which is using this Store
15399          * as a Record cache should refresh its view.
15400          * @param {Store} this
15401          */
15402         datachanged : true,
15403         /**
15404          * @event metachange
15405          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15406          * @param {Store} this
15407          * @param {Object} meta The JSON metadata
15408          */
15409         metachange : true,
15410         /**
15411          * @event add
15412          * Fires when Records have been added to the Store
15413          * @param {Store} this
15414          * @param {Roo.data.Record[]} records The array of Records added
15415          * @param {Number} index The index at which the record(s) were added
15416          */
15417         add : true,
15418         /**
15419          * @event remove
15420          * Fires when a Record has been removed from the Store
15421          * @param {Store} this
15422          * @param {Roo.data.Record} record The Record that was removed
15423          * @param {Number} index The index at which the record was removed
15424          */
15425         remove : true,
15426         /**
15427          * @event update
15428          * Fires when a Record has been updated
15429          * @param {Store} this
15430          * @param {Roo.data.Record} record The Record that was updated
15431          * @param {String} operation The update operation being performed.  Value may be one of:
15432          * <pre><code>
15433  Roo.data.Record.EDIT
15434  Roo.data.Record.REJECT
15435  Roo.data.Record.COMMIT
15436          * </code></pre>
15437          */
15438         update : true,
15439         /**
15440          * @event clear
15441          * Fires when the data cache has been cleared.
15442          * @param {Store} this
15443          */
15444         clear : true,
15445         /**
15446          * @event beforeload
15447          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15448          * the load action will be canceled.
15449          * @param {Store} this
15450          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15451          */
15452         beforeload : true,
15453         /**
15454          * @event beforeloadadd
15455          * Fires after a new set of Records has been loaded.
15456          * @param {Store} this
15457          * @param {Roo.data.Record[]} records The Records that were loaded
15458          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15459          */
15460         beforeloadadd : true,
15461         /**
15462          * @event load
15463          * Fires after a new set of Records has been loaded, before they are added to the store.
15464          * @param {Store} this
15465          * @param {Roo.data.Record[]} records The Records that were loaded
15466          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15467          * @params {Object} return from reader
15468          */
15469         load : true,
15470         /**
15471          * @event loadexception
15472          * Fires if an exception occurs in the Proxy during loading.
15473          * Called with the signature of the Proxy's "loadexception" event.
15474          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15475          * 
15476          * @param {Proxy} 
15477          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15478          * @param {Object} opts - load Options
15479          * @param {Object} jsonData from your request (normally this contains the Exception)
15480          */
15481         loadexception : true
15482     });
15483     
15484     if(this.proxy){
15485         this.proxy = Roo.factory(this.proxy, Roo.data);
15486         this.proxy.xmodule = this.xmodule || false;
15487         this.relayEvents(this.proxy,  ["loadexception"]);
15488     }
15489     this.sortToggle = {};
15490     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15491
15492     Roo.data.Store.superclass.constructor.call(this);
15493
15494     if(this.inlineData){
15495         this.loadData(this.inlineData);
15496         delete this.inlineData;
15497     }
15498 };
15499
15500 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15501      /**
15502     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15503     * without a remote query - used by combo/forms at present.
15504     */
15505     
15506     /**
15507     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15508     */
15509     /**
15510     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15511     */
15512     /**
15513     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15514     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15515     */
15516     /**
15517     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15518     * on any HTTP request
15519     */
15520     /**
15521     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15522     */
15523     /**
15524     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15525     */
15526     multiSort: false,
15527     /**
15528     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15529     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15530     */
15531     remoteSort : false,
15532
15533     /**
15534     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15535      * loaded or when a record is removed. (defaults to false).
15536     */
15537     pruneModifiedRecords : false,
15538
15539     // private
15540     lastOptions : null,
15541
15542     /**
15543      * Add Records to the Store and fires the add event.
15544      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15545      */
15546     add : function(records){
15547         records = [].concat(records);
15548         for(var i = 0, len = records.length; i < len; i++){
15549             records[i].join(this);
15550         }
15551         var index = this.data.length;
15552         this.data.addAll(records);
15553         this.fireEvent("add", this, records, index);
15554     },
15555
15556     /**
15557      * Remove a Record from the Store and fires the remove event.
15558      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15559      */
15560     remove : function(record){
15561         var index = this.data.indexOf(record);
15562         this.data.removeAt(index);
15563  
15564         if(this.pruneModifiedRecords){
15565             this.modified.remove(record);
15566         }
15567         this.fireEvent("remove", this, record, index);
15568     },
15569
15570     /**
15571      * Remove all Records from the Store and fires the clear event.
15572      */
15573     removeAll : function(){
15574         this.data.clear();
15575         if(this.pruneModifiedRecords){
15576             this.modified = [];
15577         }
15578         this.fireEvent("clear", this);
15579     },
15580
15581     /**
15582      * Inserts Records to the Store at the given index and fires the add event.
15583      * @param {Number} index The start index at which to insert the passed Records.
15584      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15585      */
15586     insert : function(index, records){
15587         records = [].concat(records);
15588         for(var i = 0, len = records.length; i < len; i++){
15589             this.data.insert(index, records[i]);
15590             records[i].join(this);
15591         }
15592         this.fireEvent("add", this, records, index);
15593     },
15594
15595     /**
15596      * Get the index within the cache of the passed Record.
15597      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15598      * @return {Number} The index of the passed Record. Returns -1 if not found.
15599      */
15600     indexOf : function(record){
15601         return this.data.indexOf(record);
15602     },
15603
15604     /**
15605      * Get the index within the cache of the Record with the passed id.
15606      * @param {String} id The id of the Record to find.
15607      * @return {Number} The index of the Record. Returns -1 if not found.
15608      */
15609     indexOfId : function(id){
15610         return this.data.indexOfKey(id);
15611     },
15612
15613     /**
15614      * Get the Record with the specified id.
15615      * @param {String} id The id of the Record to find.
15616      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15617      */
15618     getById : function(id){
15619         return this.data.key(id);
15620     },
15621
15622     /**
15623      * Get the Record at the specified index.
15624      * @param {Number} index The index of the Record to find.
15625      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15626      */
15627     getAt : function(index){
15628         return this.data.itemAt(index);
15629     },
15630
15631     /**
15632      * Returns a range of Records between specified indices.
15633      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15634      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15635      * @return {Roo.data.Record[]} An array of Records
15636      */
15637     getRange : function(start, end){
15638         return this.data.getRange(start, end);
15639     },
15640
15641     // private
15642     storeOptions : function(o){
15643         o = Roo.apply({}, o);
15644         delete o.callback;
15645         delete o.scope;
15646         this.lastOptions = o;
15647     },
15648
15649     /**
15650      * Loads the Record cache from the configured Proxy using the configured Reader.
15651      * <p>
15652      * If using remote paging, then the first load call must specify the <em>start</em>
15653      * and <em>limit</em> properties in the options.params property to establish the initial
15654      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15655      * <p>
15656      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15657      * and this call will return before the new data has been loaded. Perform any post-processing
15658      * in a callback function, or in a "load" event handler.</strong>
15659      * <p>
15660      * @param {Object} options An object containing properties which control loading options:<ul>
15661      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15662      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15663      * <pre>
15664                 {
15665                     data : data,  // array of key=>value data like JsonReader
15666                     total : data.length,
15667                     success : true
15668                     
15669                 }
15670         </pre>
15671             }.</li>
15672      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15673      * passed the following arguments:<ul>
15674      * <li>r : Roo.data.Record[]</li>
15675      * <li>options: Options object from the load call</li>
15676      * <li>success: Boolean success indicator</li></ul></li>
15677      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15678      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15679      * </ul>
15680      */
15681     load : function(options){
15682         options = options || {};
15683         if(this.fireEvent("beforeload", this, options) !== false){
15684             this.storeOptions(options);
15685             var p = Roo.apply(options.params || {}, this.baseParams);
15686             // if meta was not loaded from remote source.. try requesting it.
15687             if (!this.reader.metaFromRemote) {
15688                 p._requestMeta = 1;
15689             }
15690             if(this.sortInfo && this.remoteSort){
15691                 var pn = this.paramNames;
15692                 p[pn["sort"]] = this.sortInfo.field;
15693                 p[pn["dir"]] = this.sortInfo.direction;
15694             }
15695             if (this.multiSort) {
15696                 var pn = this.paramNames;
15697                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15698             }
15699             
15700             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15701         }
15702     },
15703
15704     /**
15705      * Reloads the Record cache from the configured Proxy using the configured Reader and
15706      * the options from the last load operation performed.
15707      * @param {Object} options (optional) An object containing properties which may override the options
15708      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15709      * the most recently used options are reused).
15710      */
15711     reload : function(options){
15712         this.load(Roo.applyIf(options||{}, this.lastOptions));
15713     },
15714
15715     // private
15716     // Called as a callback by the Reader during a load operation.
15717     loadRecords : function(o, options, success){
15718          
15719         if(!o){
15720             if(success !== false){
15721                 this.fireEvent("load", this, [], options, o);
15722             }
15723             if(options.callback){
15724                 options.callback.call(options.scope || this, [], options, false);
15725             }
15726             return;
15727         }
15728         // if data returned failure - throw an exception.
15729         if (o.success === false) {
15730             // show a message if no listener is registered.
15731             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15732                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15733             }
15734             // loadmask wil be hooked into this..
15735             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15736             return;
15737         }
15738         var r = o.records, t = o.totalRecords || r.length;
15739         
15740         this.fireEvent("beforeloadadd", this, r, options, o);
15741         
15742         if(!options || options.add !== true){
15743             if(this.pruneModifiedRecords){
15744                 this.modified = [];
15745             }
15746             for(var i = 0, len = r.length; i < len; i++){
15747                 r[i].join(this);
15748             }
15749             if(this.snapshot){
15750                 this.data = this.snapshot;
15751                 delete this.snapshot;
15752             }
15753             this.data.clear();
15754             this.data.addAll(r);
15755             this.totalLength = t;
15756             this.applySort();
15757             this.fireEvent("datachanged", this);
15758         }else{
15759             this.totalLength = Math.max(t, this.data.length+r.length);
15760             this.add(r);
15761         }
15762         
15763         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15764                 
15765             var e = new Roo.data.Record({});
15766
15767             e.set(this.parent.displayField, this.parent.emptyTitle);
15768             e.set(this.parent.valueField, '');
15769
15770             this.insert(0, e);
15771         }
15772             
15773         this.fireEvent("load", this, r, options, o);
15774         if(options.callback){
15775             options.callback.call(options.scope || this, r, options, true);
15776         }
15777     },
15778
15779
15780     /**
15781      * Loads data from a passed data block. A Reader which understands the format of the data
15782      * must have been configured in the constructor.
15783      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15784      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15785      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15786      */
15787     loadData : function(o, append){
15788         var r = this.reader.readRecords(o);
15789         this.loadRecords(r, {add: append}, true);
15790     },
15791     
15792      /**
15793      * using 'cn' the nested child reader read the child array into it's child stores.
15794      * @param {Object} rec The record with a 'children array
15795      */
15796     loadDataFromChildren : function(rec)
15797     {
15798         this.loadData(this.reader.toLoadData(rec));
15799     },
15800     
15801
15802     /**
15803      * Gets the number of cached records.
15804      * <p>
15805      * <em>If using paging, this may not be the total size of the dataset. If the data object
15806      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15807      * the data set size</em>
15808      */
15809     getCount : function(){
15810         return this.data.length || 0;
15811     },
15812
15813     /**
15814      * Gets the total number of records in the dataset as returned by the server.
15815      * <p>
15816      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15817      * the dataset size</em>
15818      */
15819     getTotalCount : function(){
15820         return this.totalLength || 0;
15821     },
15822
15823     /**
15824      * Returns the sort state of the Store as an object with two properties:
15825      * <pre><code>
15826  field {String} The name of the field by which the Records are sorted
15827  direction {String} The sort order, "ASC" or "DESC"
15828      * </code></pre>
15829      */
15830     getSortState : function(){
15831         return this.sortInfo;
15832     },
15833
15834     // private
15835     applySort : function(){
15836         if(this.sortInfo && !this.remoteSort){
15837             var s = this.sortInfo, f = s.field;
15838             var st = this.fields.get(f).sortType;
15839             var fn = function(r1, r2){
15840                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15841                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15842             };
15843             this.data.sort(s.direction, fn);
15844             if(this.snapshot && this.snapshot != this.data){
15845                 this.snapshot.sort(s.direction, fn);
15846             }
15847         }
15848     },
15849
15850     /**
15851      * Sets the default sort column and order to be used by the next load operation.
15852      * @param {String} fieldName The name of the field to sort by.
15853      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15854      */
15855     setDefaultSort : function(field, dir){
15856         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15857     },
15858
15859     /**
15860      * Sort the Records.
15861      * If remote sorting is used, the sort is performed on the server, and the cache is
15862      * reloaded. If local sorting is used, the cache is sorted internally.
15863      * @param {String} fieldName The name of the field to sort by.
15864      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15865      */
15866     sort : function(fieldName, dir){
15867         var f = this.fields.get(fieldName);
15868         if(!dir){
15869             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15870             
15871             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15872                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15873             }else{
15874                 dir = f.sortDir;
15875             }
15876         }
15877         this.sortToggle[f.name] = dir;
15878         this.sortInfo = {field: f.name, direction: dir};
15879         if(!this.remoteSort){
15880             this.applySort();
15881             this.fireEvent("datachanged", this);
15882         }else{
15883             this.load(this.lastOptions);
15884         }
15885     },
15886
15887     /**
15888      * Calls the specified function for each of the Records in the cache.
15889      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15890      * Returning <em>false</em> aborts and exits the iteration.
15891      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15892      */
15893     each : function(fn, scope){
15894         this.data.each(fn, scope);
15895     },
15896
15897     /**
15898      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15899      * (e.g., during paging).
15900      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15901      */
15902     getModifiedRecords : function(){
15903         return this.modified;
15904     },
15905
15906     // private
15907     createFilterFn : function(property, value, anyMatch){
15908         if(!value.exec){ // not a regex
15909             value = String(value);
15910             if(value.length == 0){
15911                 return false;
15912             }
15913             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15914         }
15915         return function(r){
15916             return value.test(r.data[property]);
15917         };
15918     },
15919
15920     /**
15921      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15922      * @param {String} property A field on your records
15923      * @param {Number} start The record index to start at (defaults to 0)
15924      * @param {Number} end The last record index to include (defaults to length - 1)
15925      * @return {Number} The sum
15926      */
15927     sum : function(property, start, end){
15928         var rs = this.data.items, v = 0;
15929         start = start || 0;
15930         end = (end || end === 0) ? end : rs.length-1;
15931
15932         for(var i = start; i <= end; i++){
15933             v += (rs[i].data[property] || 0);
15934         }
15935         return v;
15936     },
15937
15938     /**
15939      * Filter the records by a specified property.
15940      * @param {String} field A field on your records
15941      * @param {String/RegExp} value Either a string that the field
15942      * should start with or a RegExp to test against the field
15943      * @param {Boolean} anyMatch True to match any part not just the beginning
15944      */
15945     filter : function(property, value, anyMatch){
15946         var fn = this.createFilterFn(property, value, anyMatch);
15947         return fn ? this.filterBy(fn) : this.clearFilter();
15948     },
15949
15950     /**
15951      * Filter by a function. The specified function will be called with each
15952      * record in this data source. If the function returns true the record is included,
15953      * otherwise it is filtered.
15954      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15955      * @param {Object} scope (optional) The scope of the function (defaults to this)
15956      */
15957     filterBy : function(fn, scope){
15958         this.snapshot = this.snapshot || this.data;
15959         this.data = this.queryBy(fn, scope||this);
15960         this.fireEvent("datachanged", this);
15961     },
15962
15963     /**
15964      * Query the records by a specified property.
15965      * @param {String} field A field on your records
15966      * @param {String/RegExp} value Either a string that the field
15967      * should start with or a RegExp to test against the field
15968      * @param {Boolean} anyMatch True to match any part not just the beginning
15969      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15970      */
15971     query : function(property, value, anyMatch){
15972         var fn = this.createFilterFn(property, value, anyMatch);
15973         return fn ? this.queryBy(fn) : this.data.clone();
15974     },
15975
15976     /**
15977      * Query by a function. The specified function will be called with each
15978      * record in this data source. If the function returns true the record is included
15979      * in the results.
15980      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15981      * @param {Object} scope (optional) The scope of the function (defaults to this)
15982       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15983      **/
15984     queryBy : function(fn, scope){
15985         var data = this.snapshot || this.data;
15986         return data.filterBy(fn, scope||this);
15987     },
15988
15989     /**
15990      * Collects unique values for a particular dataIndex from this store.
15991      * @param {String} dataIndex The property to collect
15992      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15993      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15994      * @return {Array} An array of the unique values
15995      **/
15996     collect : function(dataIndex, allowNull, bypassFilter){
15997         var d = (bypassFilter === true && this.snapshot) ?
15998                 this.snapshot.items : this.data.items;
15999         var v, sv, r = [], l = {};
16000         for(var i = 0, len = d.length; i < len; i++){
16001             v = d[i].data[dataIndex];
16002             sv = String(v);
16003             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
16004                 l[sv] = true;
16005                 r[r.length] = v;
16006             }
16007         }
16008         return r;
16009     },
16010
16011     /**
16012      * Revert to a view of the Record cache with no filtering applied.
16013      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
16014      */
16015     clearFilter : function(suppressEvent){
16016         if(this.snapshot && this.snapshot != this.data){
16017             this.data = this.snapshot;
16018             delete this.snapshot;
16019             if(suppressEvent !== true){
16020                 this.fireEvent("datachanged", this);
16021             }
16022         }
16023     },
16024
16025     // private
16026     afterEdit : function(record){
16027         if(this.modified.indexOf(record) == -1){
16028             this.modified.push(record);
16029         }
16030         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
16031     },
16032     
16033     // private
16034     afterReject : function(record){
16035         this.modified.remove(record);
16036         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
16037     },
16038
16039     // private
16040     afterCommit : function(record){
16041         this.modified.remove(record);
16042         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
16043     },
16044
16045     /**
16046      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
16047      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
16048      */
16049     commitChanges : function(){
16050         var m = this.modified.slice(0);
16051         this.modified = [];
16052         for(var i = 0, len = m.length; i < len; i++){
16053             m[i].commit();
16054         }
16055     },
16056
16057     /**
16058      * Cancel outstanding changes on all changed records.
16059      */
16060     rejectChanges : function(){
16061         var m = this.modified.slice(0);
16062         this.modified = [];
16063         for(var i = 0, len = m.length; i < len; i++){
16064             m[i].reject();
16065         }
16066     },
16067
16068     onMetaChange : function(meta, rtype, o){
16069         this.recordType = rtype;
16070         this.fields = rtype.prototype.fields;
16071         delete this.snapshot;
16072         this.sortInfo = meta.sortInfo || this.sortInfo;
16073         this.modified = [];
16074         this.fireEvent('metachange', this, this.reader.meta);
16075     },
16076     
16077     moveIndex : function(data, type)
16078     {
16079         var index = this.indexOf(data);
16080         
16081         var newIndex = index + type;
16082         
16083         this.remove(data);
16084         
16085         this.insert(newIndex, data);
16086         
16087     }
16088 });/*
16089  * Based on:
16090  * Ext JS Library 1.1.1
16091  * Copyright(c) 2006-2007, Ext JS, LLC.
16092  *
16093  * Originally Released Under LGPL - original licence link has changed is not relivant.
16094  *
16095  * Fork - LGPL
16096  * <script type="text/javascript">
16097  */
16098
16099 /**
16100  * @class Roo.data.SimpleStore
16101  * @extends Roo.data.Store
16102  * Small helper class to make creating Stores from Array data easier.
16103  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
16104  * @cfg {Array} fields An array of field definition objects, or field name strings.
16105  * @cfg {Object} an existing reader (eg. copied from another store)
16106  * @cfg {Array} data The multi-dimensional array of data
16107  * @cfg {Roo.data.DataProxy} proxy [not-required]  
16108  * @cfg {Roo.data.Reader} reader  [not-required] 
16109  * @constructor
16110  * @param {Object} config
16111  */
16112 Roo.data.SimpleStore = function(config)
16113 {
16114     Roo.data.SimpleStore.superclass.constructor.call(this, {
16115         isLocal : true,
16116         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
16117                 id: config.id
16118             },
16119             Roo.data.Record.create(config.fields)
16120         ),
16121         proxy : new Roo.data.MemoryProxy(config.data)
16122     });
16123     this.load();
16124 };
16125 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
16126  * Based on:
16127  * Ext JS Library 1.1.1
16128  * Copyright(c) 2006-2007, Ext JS, LLC.
16129  *
16130  * Originally Released Under LGPL - original licence link has changed is not relivant.
16131  *
16132  * Fork - LGPL
16133  * <script type="text/javascript">
16134  */
16135
16136 /**
16137 /**
16138  * @extends Roo.data.Store
16139  * @class Roo.data.JsonStore
16140  * Small helper class to make creating Stores for JSON data easier. <br/>
16141 <pre><code>
16142 var store = new Roo.data.JsonStore({
16143     url: 'get-images.php',
16144     root: 'images',
16145     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
16146 });
16147 </code></pre>
16148  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
16149  * JsonReader and HttpProxy (unless inline data is provided).</b>
16150  * @cfg {Array} fields An array of field definition objects, or field name strings.
16151  * @constructor
16152  * @param {Object} config
16153  */
16154 Roo.data.JsonStore = function(c){
16155     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
16156         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
16157         reader: new Roo.data.JsonReader(c, c.fields)
16158     }));
16159 };
16160 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
16161  * Based on:
16162  * Ext JS Library 1.1.1
16163  * Copyright(c) 2006-2007, Ext JS, LLC.
16164  *
16165  * Originally Released Under LGPL - original licence link has changed is not relivant.
16166  *
16167  * Fork - LGPL
16168  * <script type="text/javascript">
16169  */
16170
16171  
16172 Roo.data.Field = function(config){
16173     if(typeof config == "string"){
16174         config = {name: config};
16175     }
16176     Roo.apply(this, config);
16177     
16178     if(!this.type){
16179         this.type = "auto";
16180     }
16181     
16182     var st = Roo.data.SortTypes;
16183     // named sortTypes are supported, here we look them up
16184     if(typeof this.sortType == "string"){
16185         this.sortType = st[this.sortType];
16186     }
16187     
16188     // set default sortType for strings and dates
16189     if(!this.sortType){
16190         switch(this.type){
16191             case "string":
16192                 this.sortType = st.asUCString;
16193                 break;
16194             case "date":
16195                 this.sortType = st.asDate;
16196                 break;
16197             default:
16198                 this.sortType = st.none;
16199         }
16200     }
16201
16202     // define once
16203     var stripRe = /[\$,%]/g;
16204
16205     // prebuilt conversion function for this field, instead of
16206     // switching every time we're reading a value
16207     if(!this.convert){
16208         var cv, dateFormat = this.dateFormat;
16209         switch(this.type){
16210             case "":
16211             case "auto":
16212             case undefined:
16213                 cv = function(v){ return v; };
16214                 break;
16215             case "string":
16216                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
16217                 break;
16218             case "int":
16219                 cv = function(v){
16220                     return v !== undefined && v !== null && v !== '' ?
16221                            parseInt(String(v).replace(stripRe, ""), 10) : '';
16222                     };
16223                 break;
16224             case "float":
16225                 cv = function(v){
16226                     return v !== undefined && v !== null && v !== '' ?
16227                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
16228                     };
16229                 break;
16230             case "bool":
16231             case "boolean":
16232                 cv = function(v){ return v === true || v === "true" || v == 1; };
16233                 break;
16234             case "date":
16235                 cv = function(v){
16236                     if(!v){
16237                         return '';
16238                     }
16239                     if(v instanceof Date){
16240                         return v;
16241                     }
16242                     if(dateFormat){
16243                         if(dateFormat == "timestamp"){
16244                             return new Date(v*1000);
16245                         }
16246                         return Date.parseDate(v, dateFormat);
16247                     }
16248                     var parsed = Date.parse(v);
16249                     return parsed ? new Date(parsed) : null;
16250                 };
16251              break;
16252             
16253         }
16254         this.convert = cv;
16255     }
16256 };
16257
16258 Roo.data.Field.prototype = {
16259     dateFormat: null,
16260     defaultValue: "",
16261     mapping: null,
16262     sortType : null,
16263     sortDir : "ASC"
16264 };/*
16265  * Based on:
16266  * Ext JS Library 1.1.1
16267  * Copyright(c) 2006-2007, Ext JS, LLC.
16268  *
16269  * Originally Released Under LGPL - original licence link has changed is not relivant.
16270  *
16271  * Fork - LGPL
16272  * <script type="text/javascript">
16273  */
16274  
16275 // Base class for reading structured data from a data source.  This class is intended to be
16276 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16277
16278 /**
16279  * @class Roo.data.DataReader
16280  * @abstract
16281  * Base class for reading structured data from a data source.  This class is intended to be
16282  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16283  */
16284
16285 Roo.data.DataReader = function(meta, recordType){
16286     
16287     this.meta = meta;
16288     
16289     this.recordType = recordType instanceof Array ? 
16290         Roo.data.Record.create(recordType) : recordType;
16291 };
16292
16293 Roo.data.DataReader.prototype = {
16294     
16295     
16296     readerType : 'Data',
16297      /**
16298      * Create an empty record
16299      * @param {Object} data (optional) - overlay some values
16300      * @return {Roo.data.Record} record created.
16301      */
16302     newRow :  function(d) {
16303         var da =  {};
16304         this.recordType.prototype.fields.each(function(c) {
16305             switch( c.type) {
16306                 case 'int' : da[c.name] = 0; break;
16307                 case 'date' : da[c.name] = new Date(); break;
16308                 case 'float' : da[c.name] = 0.0; break;
16309                 case 'boolean' : da[c.name] = false; break;
16310                 default : da[c.name] = ""; break;
16311             }
16312             
16313         });
16314         return new this.recordType(Roo.apply(da, d));
16315     }
16316     
16317     
16318 };/*
16319  * Based on:
16320  * Ext JS Library 1.1.1
16321  * Copyright(c) 2006-2007, Ext JS, LLC.
16322  *
16323  * Originally Released Under LGPL - original licence link has changed is not relivant.
16324  *
16325  * Fork - LGPL
16326  * <script type="text/javascript">
16327  */
16328
16329 /**
16330  * @class Roo.data.DataProxy
16331  * @extends Roo.util.Observable
16332  * @abstract
16333  * This class is an abstract base class for implementations which provide retrieval of
16334  * unformatted data objects.<br>
16335  * <p>
16336  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16337  * (of the appropriate type which knows how to parse the data object) to provide a block of
16338  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16339  * <p>
16340  * Custom implementations must implement the load method as described in
16341  * {@link Roo.data.HttpProxy#load}.
16342  */
16343 Roo.data.DataProxy = function(){
16344     this.addEvents({
16345         /**
16346          * @event beforeload
16347          * Fires before a network request is made to retrieve a data object.
16348          * @param {Object} This DataProxy object.
16349          * @param {Object} params The params parameter to the load function.
16350          */
16351         beforeload : true,
16352         /**
16353          * @event load
16354          * Fires before the load method's callback is called.
16355          * @param {Object} This DataProxy object.
16356          * @param {Object} o The data object.
16357          * @param {Object} arg The callback argument object passed to the load function.
16358          */
16359         load : true,
16360         /**
16361          * @event loadexception
16362          * Fires if an Exception occurs during data retrieval.
16363          * @param {Object} This DataProxy object.
16364          * @param {Object} o The data object.
16365          * @param {Object} arg The callback argument object passed to the load function.
16366          * @param {Object} e The Exception.
16367          */
16368         loadexception : true
16369     });
16370     Roo.data.DataProxy.superclass.constructor.call(this);
16371 };
16372
16373 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16374
16375     /**
16376      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16377      */
16378 /*
16379  * Based on:
16380  * Ext JS Library 1.1.1
16381  * Copyright(c) 2006-2007, Ext JS, LLC.
16382  *
16383  * Originally Released Under LGPL - original licence link has changed is not relivant.
16384  *
16385  * Fork - LGPL
16386  * <script type="text/javascript">
16387  */
16388 /**
16389  * @class Roo.data.MemoryProxy
16390  * @extends Roo.data.DataProxy
16391  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16392  * to the Reader when its load method is called.
16393  * @constructor
16394  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16395  */
16396 Roo.data.MemoryProxy = function(config){
16397     var data = config;
16398     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16399         data = config.data;
16400     }
16401     Roo.data.MemoryProxy.superclass.constructor.call(this);
16402     this.data = data;
16403 };
16404
16405 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16406     
16407     /**
16408      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16409      */
16410     /**
16411      * Load data from the requested source (in this case an in-memory
16412      * data object passed to the constructor), read the data object into
16413      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16414      * process that block using the passed callback.
16415      * @param {Object} params This parameter is not used by the MemoryProxy class.
16416      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16417      * object into a block of Roo.data.Records.
16418      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16419      * The function must be passed <ul>
16420      * <li>The Record block object</li>
16421      * <li>The "arg" argument from the load function</li>
16422      * <li>A boolean success indicator</li>
16423      * </ul>
16424      * @param {Object} scope The scope in which to call the callback
16425      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16426      */
16427     load : function(params, reader, callback, scope, arg){
16428         params = params || {};
16429         var result;
16430         try {
16431             result = reader.readRecords(params.data ? params.data :this.data);
16432         }catch(e){
16433             this.fireEvent("loadexception", this, arg, null, e);
16434             callback.call(scope, null, arg, false);
16435             return;
16436         }
16437         callback.call(scope, result, arg, true);
16438     },
16439     
16440     // private
16441     update : function(params, records){
16442         
16443     }
16444 });/*
16445  * Based on:
16446  * Ext JS Library 1.1.1
16447  * Copyright(c) 2006-2007, Ext JS, LLC.
16448  *
16449  * Originally Released Under LGPL - original licence link has changed is not relivant.
16450  *
16451  * Fork - LGPL
16452  * <script type="text/javascript">
16453  */
16454 /**
16455  * @class Roo.data.HttpProxy
16456  * @extends Roo.data.DataProxy
16457  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16458  * configured to reference a certain URL.<br><br>
16459  * <p>
16460  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16461  * from which the running page was served.<br><br>
16462  * <p>
16463  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16464  * <p>
16465  * Be aware that to enable the browser to parse an XML document, the server must set
16466  * the Content-Type header in the HTTP response to "text/xml".
16467  * @constructor
16468  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16469  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16470  * will be used to make the request.
16471  */
16472 Roo.data.HttpProxy = function(conn){
16473     Roo.data.HttpProxy.superclass.constructor.call(this);
16474     // is conn a conn config or a real conn?
16475     this.conn = conn;
16476     this.useAjax = !conn || !conn.events;
16477   
16478 };
16479
16480 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16481     // thse are take from connection...
16482     
16483     /**
16484      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
16485      */
16486     /**
16487      * @cfg {Object} extraParams  An object containing properties which are used as
16488      * extra parameters to each request made by this object. (defaults to undefined)
16489      */
16490     /**
16491      * @cfg {Object} defaultHeaders   An object containing request headers which are added
16492      *  to each request made by this object. (defaults to undefined)
16493      */
16494     /**
16495      * @cfg {String} method (GET|POST)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16496      */
16497     /**
16498      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16499      */
16500      /**
16501      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16502      * @type Boolean
16503      */
16504   
16505
16506     /**
16507      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16508      * @type Boolean
16509      */
16510     /**
16511      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16512      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16513      * a finer-grained basis than the DataProxy events.
16514      */
16515     getConnection : function(){
16516         return this.useAjax ? Roo.Ajax : this.conn;
16517     },
16518
16519     /**
16520      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16521      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16522      * process that block using the passed callback.
16523      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16524      * for the request to the remote server.
16525      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16526      * object into a block of Roo.data.Records.
16527      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16528      * The function must be passed <ul>
16529      * <li>The Record block object</li>
16530      * <li>The "arg" argument from the load function</li>
16531      * <li>A boolean success indicator</li>
16532      * </ul>
16533      * @param {Object} scope The scope in which to call the callback
16534      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16535      */
16536     load : function(params, reader, callback, scope, arg){
16537         if(this.fireEvent("beforeload", this, params) !== false){
16538             var  o = {
16539                 params : params || {},
16540                 request: {
16541                     callback : callback,
16542                     scope : scope,
16543                     arg : arg
16544                 },
16545                 reader: reader,
16546                 callback : this.loadResponse,
16547                 scope: this
16548             };
16549             if(this.useAjax){
16550                 Roo.applyIf(o, this.conn);
16551                 if(this.activeRequest){
16552                     Roo.Ajax.abort(this.activeRequest);
16553                 }
16554                 this.activeRequest = Roo.Ajax.request(o);
16555             }else{
16556                 this.conn.request(o);
16557             }
16558         }else{
16559             callback.call(scope||this, null, arg, false);
16560         }
16561     },
16562
16563     // private
16564     loadResponse : function(o, success, response){
16565         delete this.activeRequest;
16566         if(!success){
16567             this.fireEvent("loadexception", this, o, response);
16568             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16569             return;
16570         }
16571         var result;
16572         try {
16573             result = o.reader.read(response);
16574         }catch(e){
16575             o.success = false;
16576             o.raw = { errorMsg : response.responseText };
16577             this.fireEvent("loadexception", this, o, response, e);
16578             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16579             return;
16580         }
16581         
16582         this.fireEvent("load", this, o, o.request.arg);
16583         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16584     },
16585
16586     // private
16587     update : function(dataSet){
16588
16589     },
16590
16591     // private
16592     updateResponse : function(dataSet){
16593
16594     }
16595 });/*
16596  * Based on:
16597  * Ext JS Library 1.1.1
16598  * Copyright(c) 2006-2007, Ext JS, LLC.
16599  *
16600  * Originally Released Under LGPL - original licence link has changed is not relivant.
16601  *
16602  * Fork - LGPL
16603  * <script type="text/javascript">
16604  */
16605
16606 /**
16607  * @class Roo.data.ScriptTagProxy
16608  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16609  * other than the originating domain of the running page.<br><br>
16610  * <p>
16611  * <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
16612  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16613  * <p>
16614  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16615  * source code that is used as the source inside a &lt;script> tag.<br><br>
16616  * <p>
16617  * In order for the browser to process the returned data, the server must wrap the data object
16618  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16619  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16620  * depending on whether the callback name was passed:
16621  * <p>
16622  * <pre><code>
16623 boolean scriptTag = false;
16624 String cb = request.getParameter("callback");
16625 if (cb != null) {
16626     scriptTag = true;
16627     response.setContentType("text/javascript");
16628 } else {
16629     response.setContentType("application/x-json");
16630 }
16631 Writer out = response.getWriter();
16632 if (scriptTag) {
16633     out.write(cb + "(");
16634 }
16635 out.print(dataBlock.toJsonString());
16636 if (scriptTag) {
16637     out.write(");");
16638 }
16639 </pre></code>
16640  *
16641  * @constructor
16642  * @param {Object} config A configuration object.
16643  */
16644 Roo.data.ScriptTagProxy = function(config){
16645     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16646     Roo.apply(this, config);
16647     this.head = document.getElementsByTagName("head")[0];
16648 };
16649
16650 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16651
16652 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16653     /**
16654      * @cfg {String} url The URL from which to request the data object.
16655      */
16656     /**
16657      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16658      */
16659     timeout : 30000,
16660     /**
16661      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16662      * the server the name of the callback function set up by the load call to process the returned data object.
16663      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16664      * javascript output which calls this named function passing the data object as its only parameter.
16665      */
16666     callbackParam : "callback",
16667     /**
16668      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16669      * name to the request.
16670      */
16671     nocache : true,
16672
16673     /**
16674      * Load data from the configured URL, read the data object into
16675      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16676      * process that block using the passed callback.
16677      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16678      * for the request to the remote server.
16679      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16680      * object into a block of Roo.data.Records.
16681      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16682      * The function must be passed <ul>
16683      * <li>The Record block object</li>
16684      * <li>The "arg" argument from the load function</li>
16685      * <li>A boolean success indicator</li>
16686      * </ul>
16687      * @param {Object} scope The scope in which to call the callback
16688      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16689      */
16690     load : function(params, reader, callback, scope, arg){
16691         if(this.fireEvent("beforeload", this, params) !== false){
16692
16693             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16694
16695             var url = this.url;
16696             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16697             if(this.nocache){
16698                 url += "&_dc=" + (new Date().getTime());
16699             }
16700             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16701             var trans = {
16702                 id : transId,
16703                 cb : "stcCallback"+transId,
16704                 scriptId : "stcScript"+transId,
16705                 params : params,
16706                 arg : arg,
16707                 url : url,
16708                 callback : callback,
16709                 scope : scope,
16710                 reader : reader
16711             };
16712             var conn = this;
16713
16714             window[trans.cb] = function(o){
16715                 conn.handleResponse(o, trans);
16716             };
16717
16718             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16719
16720             if(this.autoAbort !== false){
16721                 this.abort();
16722             }
16723
16724             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16725
16726             var script = document.createElement("script");
16727             script.setAttribute("src", url);
16728             script.setAttribute("type", "text/javascript");
16729             script.setAttribute("id", trans.scriptId);
16730             this.head.appendChild(script);
16731
16732             this.trans = trans;
16733         }else{
16734             callback.call(scope||this, null, arg, false);
16735         }
16736     },
16737
16738     // private
16739     isLoading : function(){
16740         return this.trans ? true : false;
16741     },
16742
16743     /**
16744      * Abort the current server request.
16745      */
16746     abort : function(){
16747         if(this.isLoading()){
16748             this.destroyTrans(this.trans);
16749         }
16750     },
16751
16752     // private
16753     destroyTrans : function(trans, isLoaded){
16754         this.head.removeChild(document.getElementById(trans.scriptId));
16755         clearTimeout(trans.timeoutId);
16756         if(isLoaded){
16757             window[trans.cb] = undefined;
16758             try{
16759                 delete window[trans.cb];
16760             }catch(e){}
16761         }else{
16762             // if hasn't been loaded, wait for load to remove it to prevent script error
16763             window[trans.cb] = function(){
16764                 window[trans.cb] = undefined;
16765                 try{
16766                     delete window[trans.cb];
16767                 }catch(e){}
16768             };
16769         }
16770     },
16771
16772     // private
16773     handleResponse : function(o, trans){
16774         this.trans = false;
16775         this.destroyTrans(trans, true);
16776         var result;
16777         try {
16778             result = trans.reader.readRecords(o);
16779         }catch(e){
16780             this.fireEvent("loadexception", this, o, trans.arg, e);
16781             trans.callback.call(trans.scope||window, null, trans.arg, false);
16782             return;
16783         }
16784         this.fireEvent("load", this, o, trans.arg);
16785         trans.callback.call(trans.scope||window, result, trans.arg, true);
16786     },
16787
16788     // private
16789     handleFailure : function(trans){
16790         this.trans = false;
16791         this.destroyTrans(trans, false);
16792         this.fireEvent("loadexception", this, null, trans.arg);
16793         trans.callback.call(trans.scope||window, null, trans.arg, false);
16794     }
16795 });/*
16796  * Based on:
16797  * Ext JS Library 1.1.1
16798  * Copyright(c) 2006-2007, Ext JS, LLC.
16799  *
16800  * Originally Released Under LGPL - original licence link has changed is not relivant.
16801  *
16802  * Fork - LGPL
16803  * <script type="text/javascript">
16804  */
16805
16806 /**
16807  * @class Roo.data.JsonReader
16808  * @extends Roo.data.DataReader
16809  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16810  * based on mappings in a provided Roo.data.Record constructor.
16811  * 
16812  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16813  * in the reply previously. 
16814  * 
16815  * <p>
16816  * Example code:
16817  * <pre><code>
16818 var RecordDef = Roo.data.Record.create([
16819     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16820     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16821 ]);
16822 var myReader = new Roo.data.JsonReader({
16823     totalProperty: "results",    // The property which contains the total dataset size (optional)
16824     root: "rows",                // The property which contains an Array of row objects
16825     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16826 }, RecordDef);
16827 </code></pre>
16828  * <p>
16829  * This would consume a JSON file like this:
16830  * <pre><code>
16831 { 'results': 2, 'rows': [
16832     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16833     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16834 }
16835 </code></pre>
16836  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16837  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16838  * paged from the remote server.
16839  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16840  * @cfg {String} root name of the property which contains the Array of row objects.
16841  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16842  * @cfg {Array} fields Array of field definition objects
16843  * @constructor
16844  * Create a new JsonReader
16845  * @param {Object} meta Metadata configuration options
16846  * @param {Object} recordType Either an Array of field definition objects,
16847  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16848  */
16849 Roo.data.JsonReader = function(meta, recordType){
16850     
16851     meta = meta || {};
16852     // set some defaults:
16853     Roo.applyIf(meta, {
16854         totalProperty: 'total',
16855         successProperty : 'success',
16856         root : 'data',
16857         id : 'id'
16858     });
16859     
16860     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16861 };
16862 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16863     
16864     readerType : 'Json',
16865     
16866     /**
16867      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16868      * Used by Store query builder to append _requestMeta to params.
16869      * 
16870      */
16871     metaFromRemote : false,
16872     /**
16873      * This method is only used by a DataProxy which has retrieved data from a remote server.
16874      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16875      * @return {Object} data A data block which is used by an Roo.data.Store object as
16876      * a cache of Roo.data.Records.
16877      */
16878     read : function(response){
16879         var json = response.responseText;
16880        
16881         var o = /* eval:var:o */ eval("("+json+")");
16882         if(!o) {
16883             throw {message: "JsonReader.read: Json object not found"};
16884         }
16885         
16886         if(o.metaData){
16887             
16888             delete this.ef;
16889             this.metaFromRemote = true;
16890             this.meta = o.metaData;
16891             this.recordType = Roo.data.Record.create(o.metaData.fields);
16892             this.onMetaChange(this.meta, this.recordType, o);
16893         }
16894         return this.readRecords(o);
16895     },
16896
16897     // private function a store will implement
16898     onMetaChange : function(meta, recordType, o){
16899
16900     },
16901
16902     /**
16903          * @ignore
16904          */
16905     simpleAccess: function(obj, subsc) {
16906         return obj[subsc];
16907     },
16908
16909         /**
16910          * @ignore
16911          */
16912     getJsonAccessor: function(){
16913         var re = /[\[\.]/;
16914         return function(expr) {
16915             try {
16916                 return(re.test(expr))
16917                     ? new Function("obj", "return obj." + expr)
16918                     : function(obj){
16919                         return obj[expr];
16920                     };
16921             } catch(e){}
16922             return Roo.emptyFn;
16923         };
16924     }(),
16925
16926     /**
16927      * Create a data block containing Roo.data.Records from an XML document.
16928      * @param {Object} o An object which contains an Array of row objects in the property specified
16929      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16930      * which contains the total size of the dataset.
16931      * @return {Object} data A data block which is used by an Roo.data.Store object as
16932      * a cache of Roo.data.Records.
16933      */
16934     readRecords : function(o){
16935         /**
16936          * After any data loads, the raw JSON data is available for further custom processing.
16937          * @type Object
16938          */
16939         this.o = o;
16940         var s = this.meta, Record = this.recordType,
16941             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16942
16943 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16944         if (!this.ef) {
16945             if(s.totalProperty) {
16946                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16947                 }
16948                 if(s.successProperty) {
16949                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16950                 }
16951                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16952                 if (s.id) {
16953                         var g = this.getJsonAccessor(s.id);
16954                         this.getId = function(rec) {
16955                                 var r = g(rec);  
16956                                 return (r === undefined || r === "") ? null : r;
16957                         };
16958                 } else {
16959                         this.getId = function(){return null;};
16960                 }
16961             this.ef = [];
16962             for(var jj = 0; jj < fl; jj++){
16963                 f = fi[jj];
16964                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16965                 this.ef[jj] = this.getJsonAccessor(map);
16966             }
16967         }
16968
16969         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16970         if(s.totalProperty){
16971             var vt = parseInt(this.getTotal(o), 10);
16972             if(!isNaN(vt)){
16973                 totalRecords = vt;
16974             }
16975         }
16976         if(s.successProperty){
16977             var vs = this.getSuccess(o);
16978             if(vs === false || vs === 'false'){
16979                 success = false;
16980             }
16981         }
16982         var records = [];
16983         for(var i = 0; i < c; i++){
16984             var n = root[i];
16985             var values = {};
16986             var id = this.getId(n);
16987             for(var j = 0; j < fl; j++){
16988                 f = fi[j];
16989                                 var v = this.ef[j](n);
16990                                 if (!f.convert) {
16991                                         Roo.log('missing convert for ' + f.name);
16992                                         Roo.log(f);
16993                                         continue;
16994                                 }
16995                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16996             }
16997                         if (!Record) {
16998                                 return {
16999                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
17000                                         success : false,
17001                                         records : [],
17002                                         totalRecords : 0
17003                                 };
17004                         }
17005             var record = new Record(values, id);
17006             record.json = n;
17007             records[i] = record;
17008         }
17009         return {
17010             raw : o,
17011             success : success,
17012             records : records,
17013             totalRecords : totalRecords
17014         };
17015     },
17016     // used when loading children.. @see loadDataFromChildren
17017     toLoadData: function(rec)
17018     {
17019         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
17020         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
17021         return { data : data, total : data.length };
17022         
17023     }
17024 });/*
17025  * Based on:
17026  * Ext JS Library 1.1.1
17027  * Copyright(c) 2006-2007, Ext JS, LLC.
17028  *
17029  * Originally Released Under LGPL - original licence link has changed is not relivant.
17030  *
17031  * Fork - LGPL
17032  * <script type="text/javascript">
17033  */
17034
17035 /**
17036  * @class Roo.data.ArrayReader
17037  * @extends Roo.data.DataReader
17038  * Data reader class to create an Array of Roo.data.Record objects from an Array.
17039  * Each element of that Array represents a row of data fields. The
17040  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
17041  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
17042  * <p>
17043  * Example code:.
17044  * <pre><code>
17045 var RecordDef = Roo.data.Record.create([
17046     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
17047     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
17048 ]);
17049 var myReader = new Roo.data.ArrayReader({
17050     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
17051 }, RecordDef);
17052 </code></pre>
17053  * <p>
17054  * This would consume an Array like this:
17055  * <pre><code>
17056 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
17057   </code></pre>
17058  
17059  * @constructor
17060  * Create a new JsonReader
17061  * @param {Object} meta Metadata configuration options.
17062  * @param {Object|Array} recordType Either an Array of field definition objects
17063  * 
17064  * @cfg {Array} fields Array of field definition objects
17065  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
17066  * as specified to {@link Roo.data.Record#create},
17067  * or an {@link Roo.data.Record} object
17068  *
17069  * 
17070  * created using {@link Roo.data.Record#create}.
17071  */
17072 Roo.data.ArrayReader = function(meta, recordType)
17073 {    
17074     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
17075 };
17076
17077 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
17078     
17079       /**
17080      * Create a data block containing Roo.data.Records from an XML document.
17081      * @param {Object} o An Array of row objects which represents the dataset.
17082      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
17083      * a cache of Roo.data.Records.
17084      */
17085     readRecords : function(o)
17086     {
17087         var sid = this.meta ? this.meta.id : null;
17088         var recordType = this.recordType, fields = recordType.prototype.fields;
17089         var records = [];
17090         var root = o;
17091         for(var i = 0; i < root.length; i++){
17092             var n = root[i];
17093             var values = {};
17094             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
17095             for(var j = 0, jlen = fields.length; j < jlen; j++){
17096                 var f = fields.items[j];
17097                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
17098                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
17099                 v = f.convert(v);
17100                 values[f.name] = v;
17101             }
17102             var record = new recordType(values, id);
17103             record.json = n;
17104             records[records.length] = record;
17105         }
17106         return {
17107             records : records,
17108             totalRecords : records.length
17109         };
17110     },
17111     // used when loading children.. @see loadDataFromChildren
17112     toLoadData: function(rec)
17113     {
17114         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
17115         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
17116         
17117     }
17118     
17119     
17120 });/*
17121  * - LGPL
17122  * * 
17123  */
17124
17125 /**
17126  * @class Roo.bootstrap.form.ComboBox
17127  * @extends Roo.bootstrap.form.TriggerField
17128  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
17129  * @cfg {Boolean} append (true|false) default false
17130  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
17131  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
17132  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
17133  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
17134  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
17135  * @cfg {Boolean} animate default true
17136  * @cfg {Boolean} emptyResultText only for touch device
17137  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
17138  * @cfg {String} emptyTitle default ''
17139  * @cfg {Number} width fixed with? experimental
17140  * @constructor
17141  * Create a new ComboBox.
17142  * @param {Object} config Configuration options
17143  */
17144 Roo.bootstrap.form.ComboBox = function(config){
17145     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
17146     this.addEvents({
17147         /**
17148          * @event expand
17149          * Fires when the dropdown list is expanded
17150         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17151         */
17152         'expand' : true,
17153         /**
17154          * @event collapse
17155          * Fires when the dropdown list is collapsed
17156         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17157         */
17158         'collapse' : true,
17159         /**
17160          * @event beforeselect
17161          * Fires before a list item is selected. Return false to cancel the selection.
17162         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17163         * @param {Roo.data.Record} record The data record returned from the underlying store
17164         * @param {Number} index The index of the selected item in the dropdown list
17165         */
17166         'beforeselect' : true,
17167         /**
17168          * @event select
17169          * Fires when a list item is selected
17170         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17171         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
17172         * @param {Number} index The index of the selected item in the dropdown list
17173         */
17174         'select' : true,
17175         /**
17176          * @event beforequery
17177          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
17178          * The event object passed has these properties:
17179         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17180         * @param {String} query The query
17181         * @param {Boolean} forceAll true to force "all" query
17182         * @param {Boolean} cancel true to cancel the query
17183         * @param {Object} e The query event object
17184         */
17185         'beforequery': true,
17186          /**
17187          * @event add
17188          * Fires when the 'add' icon is pressed (add a listener to enable add button)
17189         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17190         */
17191         'add' : true,
17192         /**
17193          * @event edit
17194          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
17195         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17196         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
17197         */
17198         'edit' : true,
17199         /**
17200          * @event remove
17201          * Fires when the remove value from the combobox array
17202         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17203         */
17204         'remove' : true,
17205         /**
17206          * @event afterremove
17207          * Fires when the remove value from the combobox array
17208         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17209         */
17210         'afterremove' : true,
17211         /**
17212          * @event specialfilter
17213          * Fires when specialfilter
17214             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17215             */
17216         'specialfilter' : true,
17217         /**
17218          * @event tick
17219          * Fires when tick the element
17220             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17221             */
17222         'tick' : true,
17223         /**
17224          * @event touchviewdisplay
17225          * Fires when touch view require special display (default is using displayField)
17226             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
17227             * @param {Object} cfg set html .
17228             */
17229         'touchviewdisplay' : true
17230         
17231     });
17232     
17233     this.item = [];
17234     this.tickItems = [];
17235     
17236     this.selectedIndex = -1;
17237     if(this.mode == 'local'){
17238         if(config.queryDelay === undefined){
17239             this.queryDelay = 10;
17240         }
17241         if(config.minChars === undefined){
17242             this.minChars = 0;
17243         }
17244     }
17245 };
17246
17247 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
17248      
17249     /**
17250      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
17251      * rendering into an Roo.Editor, defaults to false)
17252      */
17253     /**
17254      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
17255      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
17256      */
17257     /**
17258      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
17259      */
17260     /**
17261      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
17262      * the dropdown list (defaults to undefined, with no header element)
17263      */
17264
17265      /**
17266      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
17267      */
17268      
17269      /**
17270      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17271      */
17272     listWidth: undefined,
17273     /**
17274      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17275      * mode = 'remote' or 'text' if mode = 'local')
17276      */
17277     displayField: undefined,
17278     
17279     /**
17280      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17281      * mode = 'remote' or 'value' if mode = 'local'). 
17282      * Note: use of a valueField requires the user make a selection
17283      * in order for a value to be mapped.
17284      */
17285     valueField: undefined,
17286     /**
17287      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17288      */
17289     modalTitle : '',
17290     
17291     /**
17292      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17293      * field's data value (defaults to the underlying DOM element's name)
17294      */
17295     hiddenName: undefined,
17296     /**
17297      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17298      */
17299     listClass: '',
17300     /**
17301      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17302      */
17303     selectedClass: 'active',
17304     
17305     /**
17306      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17307      */
17308     shadow:'sides',
17309     /**
17310      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17311      * anchor positions (defaults to 'tl-bl')
17312      */
17313     listAlign: 'tl-bl?',
17314     /**
17315      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17316      */
17317     // maxHeight: 300, // not used (change maxHeight in CSS. target the list using listClass)
17318     /**
17319      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17320      * query specified by the allQuery config option (defaults to 'query')
17321      */
17322     triggerAction: 'query',
17323     /**
17324      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17325      * (defaults to 4, does not apply if editable = false)
17326      */
17327     minChars : 4,
17328     /**
17329      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17330      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17331      */
17332     typeAhead: false,
17333     /**
17334      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17335      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17336      */
17337     queryDelay: 500,
17338     /**
17339      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17340      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17341      */
17342     pageSize: 0,
17343     /**
17344      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17345      * when editable = true (defaults to false)
17346      */
17347     selectOnFocus:false,
17348     /**
17349      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17350      */
17351     queryParam: 'query',
17352     /**
17353      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17354      * when mode = 'remote' (defaults to 'Loading...')
17355      */
17356     loadingText: 'Loading...',
17357     /**
17358      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17359      */
17360     resizable: false,
17361     /**
17362      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17363      */
17364     handleHeight : 8,
17365     /**
17366      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17367      * traditional select (defaults to true)
17368      */
17369     editable: true,
17370     /**
17371      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17372      */
17373     allQuery: '',
17374     /**
17375      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17376      */
17377     mode: 'remote',
17378     /**
17379      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17380      * listWidth has a higher value)
17381      */
17382     minListWidth : 70,
17383     /**
17384      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17385      * allow the user to set arbitrary text into the field (defaults to false)
17386      */
17387     forceSelection:false,
17388     /**
17389      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17390      * if typeAhead = true (defaults to 250)
17391      */
17392     typeAheadDelay : 250,
17393     /**
17394      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17395      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17396      */
17397     valueNotFoundText : undefined,
17398     /**
17399      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17400      */
17401     blockFocus : false,
17402     
17403     /**
17404      * @cfg {Boolean} disableClear Disable showing of clear button.
17405      */
17406     disableClear : false,
17407     /**
17408      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17409      */
17410     alwaysQuery : false,
17411     
17412     /**
17413      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17414      */
17415     multiple : false,
17416     
17417     /**
17418      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17419      */
17420     invalidClass : "has-warning",
17421     
17422     /**
17423      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17424      */
17425     validClass : "has-success",
17426     
17427     /**
17428      * @cfg {Boolean} specialFilter (true|false) special filter default false
17429      */
17430     specialFilter : false,
17431     
17432     /**
17433      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17434      */
17435     mobileTouchView : true,
17436     
17437     /**
17438      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17439      */
17440     useNativeIOS : false,
17441     
17442     /**
17443      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17444      */
17445     mobile_restrict_height : false,
17446     
17447     ios_options : false,
17448     
17449     //private
17450     addicon : false,
17451     editicon: false,
17452     
17453     page: 0,
17454     hasQuery: false,
17455     append: false,
17456     loadNext: false,
17457     autoFocus : true,
17458     tickable : false,
17459     btnPosition : 'right',
17460     triggerList : true,
17461     showToggleBtn : true,
17462     animate : true,
17463     emptyResultText: 'Empty',
17464     triggerText : 'Select',
17465     emptyTitle : '',
17466     width : false,
17467     
17468     // element that contains real text value.. (when hidden is used..)
17469     
17470     getAutoCreate : function()
17471     {   
17472         var cfg = false;
17473         //render
17474         /*
17475          * Render classic select for iso
17476          */
17477         
17478         if(Roo.isIOS && this.useNativeIOS){
17479             cfg = this.getAutoCreateNativeIOS();
17480             return cfg;
17481         }
17482         
17483         /*
17484          * Touch Devices
17485          */
17486         
17487         if(Roo.isTouch && this.mobileTouchView){
17488             cfg = this.getAutoCreateTouchView();
17489             return cfg;;
17490         }
17491         
17492         /*
17493          *  Normal ComboBox
17494          */
17495         if(!this.tickable){
17496             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17497             return cfg;
17498         }
17499         
17500         /*
17501          *  ComboBox with tickable selections
17502          */
17503              
17504         var align = this.labelAlign || this.parentLabelAlign();
17505         
17506         cfg = {
17507             cls : 'form-group roo-combobox-tickable' //input-group
17508         };
17509         
17510         var btn_text_select = '';
17511         var btn_text_done = '';
17512         var btn_text_cancel = '';
17513         
17514         if (this.btn_text_show) {
17515             btn_text_select = 'Select';
17516             btn_text_done = 'Done';
17517             btn_text_cancel = 'Cancel'; 
17518         }
17519         
17520         var buttons = {
17521             tag : 'div',
17522             cls : 'tickable-buttons',
17523             cn : [
17524                 {
17525                     tag : 'button',
17526                     type : 'button',
17527                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17528                     //html : this.triggerText
17529                     html: btn_text_select
17530                 },
17531                 {
17532                     tag : 'button',
17533                     type : 'button',
17534                     name : 'ok',
17535                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17536                     //html : 'Done'
17537                     html: btn_text_done
17538                 },
17539                 {
17540                     tag : 'button',
17541                     type : 'button',
17542                     name : 'cancel',
17543                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17544                     //html : 'Cancel'
17545                     html: btn_text_cancel
17546                 }
17547             ]
17548         };
17549         
17550         if(this.editable){
17551             buttons.cn.unshift({
17552                 tag: 'input',
17553                 cls: 'roo-select2-search-field-input'
17554             });
17555         }
17556         
17557         var _this = this;
17558         
17559         Roo.each(buttons.cn, function(c){
17560             if (_this.size) {
17561                 c.cls += ' btn-' + _this.size;
17562             }
17563
17564             if (_this.disabled) {
17565                 c.disabled = true;
17566             }
17567         });
17568         
17569         var box = {
17570             tag: 'div',
17571             style : 'display: contents',
17572             cn: [
17573                 {
17574                     tag: 'input',
17575                     type : 'hidden',
17576                     cls: 'form-hidden-field'
17577                 },
17578                 {
17579                     tag: 'ul',
17580                     cls: 'roo-select2-choices',
17581                     cn:[
17582                         {
17583                             tag: 'li',
17584                             cls: 'roo-select2-search-field',
17585                             cn: [
17586                                 buttons
17587                             ]
17588                         }
17589                     ]
17590                 }
17591             ]
17592         };
17593         
17594         var combobox = {
17595             cls: 'roo-select2-container input-group roo-select2-container-multi',
17596             cn: [
17597                 
17598                 box
17599 //                {
17600 //                    tag: 'ul',
17601 //                    cls: 'typeahead typeahead-long dropdown-menu',
17602 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17603 //                }
17604             ]
17605         };
17606         
17607         if(this.hasFeedback && !this.allowBlank){
17608             
17609             var feedback = {
17610                 tag: 'span',
17611                 cls: 'glyphicon form-control-feedback'
17612             };
17613
17614             combobox.cn.push(feedback);
17615         }
17616         
17617         
17618         
17619         var indicator = {
17620             tag : 'i',
17621             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17622             tooltip : 'This field is required'
17623         };
17624          
17625         if (this.allowBlank) {
17626             indicator = {
17627                 tag : 'i',
17628                 style : 'display:none'
17629             };
17630         } 
17631         if (align ==='left' && this.fieldLabel.length) {
17632             
17633             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17634             
17635             cfg.cn = [
17636                 indicator,
17637                 {
17638                     tag: 'label',
17639                     'for' :  id,
17640                     cls : 'control-label col-form-label',
17641                     html : this.fieldLabel
17642
17643                 },
17644                 {
17645                     cls : "", 
17646                     cn: [
17647                         combobox
17648                     ]
17649                 }
17650
17651             ];
17652             
17653             var labelCfg = cfg.cn[1];
17654             var contentCfg = cfg.cn[2];
17655             
17656
17657             if(this.indicatorpos == 'right'){
17658                 
17659                 cfg.cn = [
17660                     {
17661                         tag: 'label',
17662                         'for' :  id,
17663                         cls : 'control-label col-form-label',
17664                         cn : [
17665                             {
17666                                 tag : 'span',
17667                                 html : this.fieldLabel
17668                             },
17669                             indicator
17670                         ]
17671                     },
17672                     {
17673                         cls : "",
17674                         cn: [
17675                             combobox
17676                         ]
17677                     }
17678
17679                 ];
17680                 
17681                 
17682                 
17683                 labelCfg = cfg.cn[0];
17684                 contentCfg = cfg.cn[1];
17685             
17686             }
17687             
17688             if(this.labelWidth > 12){
17689                 labelCfg.style = "width: " + this.labelWidth + 'px';
17690             }
17691             if(this.width * 1 > 0){
17692                 contentCfg.style = "width: " + this.width + 'px';
17693             }
17694             if(this.labelWidth < 13 && this.labelmd == 0){
17695                 this.labelmd = this.labelWidth;
17696             }
17697             
17698             if(this.labellg > 0){
17699                 labelCfg.cls += ' col-lg-' + this.labellg;
17700                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17701             }
17702             
17703             if(this.labelmd > 0){
17704                 labelCfg.cls += ' col-md-' + this.labelmd;
17705                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17706             }
17707             
17708             if(this.labelsm > 0){
17709                 labelCfg.cls += ' col-sm-' + this.labelsm;
17710                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17711             }
17712             
17713             if(this.labelxs > 0){
17714                 labelCfg.cls += ' col-xs-' + this.labelxs;
17715                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17716             }
17717                 
17718                 
17719         } else if ( this.fieldLabel.length) {
17720 //                Roo.log(" label");
17721                  cfg.cn = [
17722                    indicator,
17723                     {
17724                         tag: 'label',
17725                         //cls : 'input-group-addon',
17726                         html : this.fieldLabel
17727                     },
17728                     combobox
17729                 ];
17730                 
17731                 if(this.indicatorpos == 'right'){
17732                     cfg.cn = [
17733                         {
17734                             tag: 'label',
17735                             //cls : 'input-group-addon',
17736                             html : this.fieldLabel
17737                         },
17738                         indicator,
17739                         combobox
17740                     ];
17741                     
17742                 }
17743
17744         } else {
17745             
17746 //                Roo.log(" no label && no align");
17747                 cfg = combobox
17748                      
17749                 
17750         }
17751          
17752         var settings=this;
17753         ['xs','sm','md','lg'].map(function(size){
17754             if (settings[size]) {
17755                 cfg.cls += ' col-' + size + '-' + settings[size];
17756             }
17757         });
17758         
17759         return cfg;
17760         
17761     },
17762     
17763     _initEventsCalled : false,
17764     
17765     // private
17766     initEvents: function()
17767     {   
17768         if (this._initEventsCalled) { // as we call render... prevent looping...
17769             return;
17770         }
17771         this._initEventsCalled = true;
17772         
17773         if (!this.store) {
17774             throw "can not find store for combo";
17775         }
17776         
17777         this.indicator = this.indicatorEl();
17778         
17779         this.store = Roo.factory(this.store, Roo.data);
17780         this.store.parent = this;
17781         
17782         // if we are building from html. then this element is so complex, that we can not really
17783         // use the rendered HTML.
17784         // so we have to trash and replace the previous code.
17785         if (Roo.XComponent.build_from_html) {
17786             // remove this element....
17787             var e = this.el.dom, k=0;
17788             while (e ) { e = e.previousSibling;  ++k;}
17789
17790             this.el.remove();
17791             
17792             this.el=false;
17793             this.rendered = false;
17794             
17795             this.render(this.parent().getChildContainer(true), k);
17796         }
17797         
17798         if(Roo.isIOS && this.useNativeIOS){
17799             this.initIOSView();
17800             return;
17801         }
17802         
17803         /*
17804          * Touch Devices
17805          */
17806         
17807         if(Roo.isTouch && this.mobileTouchView){
17808             this.initTouchView();
17809             return;
17810         }
17811         
17812         if(this.tickable){
17813             this.initTickableEvents();
17814             return;
17815         }
17816         
17817         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17818         
17819         if(this.hiddenName){
17820             
17821             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17822             
17823             this.hiddenField.dom.value =
17824                 this.hiddenValue !== undefined ? this.hiddenValue :
17825                 this.value !== undefined ? this.value : '';
17826
17827             // prevent input submission
17828             this.el.dom.removeAttribute('name');
17829             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17830              
17831              
17832         }
17833         //if(Roo.isGecko){
17834         //    this.el.dom.setAttribute('autocomplete', 'off');
17835         //}
17836         
17837         var cls = 'x-combo-list';
17838         
17839         //this.list = new Roo.Layer({
17840         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17841         //});
17842
17843         this.list.addClass(this.listClass);
17844         
17845         var _this = this;
17846         
17847         (function(){
17848             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17849             _this.list.setWidth(lw);
17850         }).defer(100);
17851         
17852         this.list.on('mouseover', this.onViewOver, this);
17853         this.list.on('mousemove', this.onViewMove, this);
17854         this.list.on('scroll', this.onViewScroll, this);
17855         
17856         /*
17857         this.list.swallowEvent('mousewheel');
17858         this.assetHeight = 0;
17859
17860         if(this.title){
17861             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17862             this.assetHeight += this.header.getHeight();
17863         }
17864
17865         this.innerList = this.list.createChild({cls:cls+'-inner'});
17866         this.innerList.on('mouseover', this.onViewOver, this);
17867         this.innerList.on('mousemove', this.onViewMove, this);
17868         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17869         
17870         if(this.allowBlank && !this.pageSize && !this.disableClear){
17871             this.footer = this.list.createChild({cls:cls+'-ft'});
17872             this.pageTb = new Roo.Toolbar(this.footer);
17873            
17874         }
17875         if(this.pageSize){
17876             this.footer = this.list.createChild({cls:cls+'-ft'});
17877             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17878                     {pageSize: this.pageSize});
17879             
17880         }
17881         
17882         if (this.pageTb && this.allowBlank && !this.disableClear) {
17883             var _this = this;
17884             this.pageTb.add(new Roo.Toolbar.Fill(), {
17885                 cls: 'x-btn-icon x-btn-clear',
17886                 text: '&#160;',
17887                 handler: function()
17888                 {
17889                     _this.collapse();
17890                     _this.clearValue();
17891                     _this.onSelect(false, -1);
17892                 }
17893             });
17894         }
17895         if (this.footer) {
17896             this.assetHeight += this.footer.getHeight();
17897         }
17898         */
17899             
17900         if(!this.tpl){
17901             this.tpl = Roo.bootstrap.version == 4 ?
17902                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17903                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17904         }
17905
17906         this.view = new Roo.View(this.list, this.tpl, {
17907             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17908         });
17909         //this.view.wrapEl.setDisplayed(false);
17910         this.view.on('click', this.onViewClick, this);
17911         
17912         
17913         this.store.on('beforeload', this.onBeforeLoad, this);
17914         this.store.on('load', this.onLoad, this);
17915         this.store.on('loadexception', this.onLoadException, this);
17916         /*
17917         if(this.resizable){
17918             this.resizer = new Roo.Resizable(this.list,  {
17919                pinned:true, handles:'se'
17920             });
17921             this.resizer.on('resize', function(r, w, h){
17922                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17923                 this.listWidth = w;
17924                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17925                 this.restrictHeight();
17926             }, this);
17927             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17928         }
17929         */
17930         if(!this.editable){
17931             this.editable = true;
17932             this.setEditable(false);
17933         }
17934         
17935         /*
17936         
17937         if (typeof(this.events.add.listeners) != 'undefined') {
17938             
17939             this.addicon = this.wrap.createChild(
17940                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17941        
17942             this.addicon.on('click', function(e) {
17943                 this.fireEvent('add', this);
17944             }, this);
17945         }
17946         if (typeof(this.events.edit.listeners) != 'undefined') {
17947             
17948             this.editicon = this.wrap.createChild(
17949                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17950             if (this.addicon) {
17951                 this.editicon.setStyle('margin-left', '40px');
17952             }
17953             this.editicon.on('click', function(e) {
17954                 
17955                 // we fire even  if inothing is selected..
17956                 this.fireEvent('edit', this, this.lastData );
17957                 
17958             }, this);
17959         }
17960         */
17961         
17962         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17963             "up" : function(e){
17964                 this.inKeyMode = true;
17965                 this.selectPrev();
17966             },
17967
17968             "down" : function(e){
17969                 if(!this.isExpanded()){
17970                     this.onTriggerClick();
17971                 }else{
17972                     this.inKeyMode = true;
17973                     this.selectNext();
17974                 }
17975             },
17976
17977             "enter" : function(e){
17978 //                this.onViewClick();
17979                 //return true;
17980                 this.collapse();
17981                 
17982                 if(this.fireEvent("specialkey", this, e)){
17983                     this.onViewClick(false);
17984                 }
17985                 
17986                 return true;
17987             },
17988
17989             "esc" : function(e){
17990                 this.collapse();
17991             },
17992
17993             "tab" : function(e){
17994                 this.collapse();
17995                 
17996                 if(this.fireEvent("specialkey", this, e)){
17997                     this.onViewClick(false);
17998                 }
17999                 
18000                 return true;
18001             },
18002
18003             scope : this,
18004
18005             doRelay : function(foo, bar, hname){
18006                 if(hname == 'down' || this.scope.isExpanded()){
18007                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18008                 }
18009                 return true;
18010             },
18011
18012             forceKeyDown: true
18013         });
18014         
18015         
18016         this.queryDelay = Math.max(this.queryDelay || 10,
18017                 this.mode == 'local' ? 10 : 250);
18018         
18019         
18020         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18021         
18022         if(this.typeAhead){
18023             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18024         }
18025         if(this.editable !== false){
18026             this.inputEl().on("keyup", this.onKeyUp, this);
18027         }
18028         if(this.forceSelection){
18029             this.inputEl().on('blur', this.doForce, this);
18030         }
18031         
18032         if(this.multiple){
18033             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18034             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18035         }
18036     },
18037     
18038     initTickableEvents: function()
18039     {   
18040         this.createList();
18041         
18042         if(this.hiddenName){
18043             
18044             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18045             
18046             this.hiddenField.dom.value =
18047                 this.hiddenValue !== undefined ? this.hiddenValue :
18048                 this.value !== undefined ? this.value : '';
18049
18050             // prevent input submission
18051             this.el.dom.removeAttribute('name');
18052             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18053              
18054              
18055         }
18056         
18057 //        this.list = this.el.select('ul.dropdown-menu',true).first();
18058         
18059         this.choices = this.el.select('ul.roo-select2-choices', true).first();
18060         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18061         if(this.triggerList){
18062             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
18063         }
18064          
18065         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
18066         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
18067         
18068         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
18069         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
18070         
18071         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
18072         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
18073         
18074         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
18075         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
18076         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
18077         
18078         this.okBtn.hide();
18079         this.cancelBtn.hide();
18080         
18081         var _this = this;
18082         
18083         (function(){
18084             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
18085             _this.list.setWidth(lw);
18086         }).defer(100);
18087         
18088         this.list.on('mouseover', this.onViewOver, this);
18089         this.list.on('mousemove', this.onViewMove, this);
18090         
18091         this.list.on('scroll', this.onViewScroll, this);
18092         
18093         if(!this.tpl){
18094             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
18095                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
18096         }
18097
18098         this.view = new Roo.View(this.list, this.tpl, {
18099             singleSelect:true,
18100             tickable:true,
18101             parent:this,
18102             store: this.store,
18103             selectedClass: this.selectedClass
18104         });
18105         
18106         //this.view.wrapEl.setDisplayed(false);
18107         this.view.on('click', this.onViewClick, this);
18108         
18109         
18110         
18111         this.store.on('beforeload', this.onBeforeLoad, this);
18112         this.store.on('load', this.onLoad, this);
18113         this.store.on('loadexception', this.onLoadException, this);
18114         
18115         if(this.editable){
18116             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
18117                 "up" : function(e){
18118                     this.inKeyMode = true;
18119                     this.selectPrev();
18120                 },
18121
18122                 "down" : function(e){
18123                     this.inKeyMode = true;
18124                     this.selectNext();
18125                 },
18126
18127                 "enter" : function(e){
18128                     if(this.fireEvent("specialkey", this, e)){
18129                         this.onViewClick(false);
18130                     }
18131                     
18132                     return true;
18133                 },
18134
18135                 "esc" : function(e){
18136                     this.onTickableFooterButtonClick(e, false, false);
18137                 },
18138
18139                 "tab" : function(e){
18140                     this.fireEvent("specialkey", this, e);
18141                     
18142                     this.onTickableFooterButtonClick(e, false, false);
18143                     
18144                     return true;
18145                 },
18146
18147                 scope : this,
18148
18149                 doRelay : function(e, fn, key){
18150                     if(this.scope.isExpanded()){
18151                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18152                     }
18153                     return true;
18154                 },
18155
18156                 forceKeyDown: true
18157             });
18158         }
18159         
18160         this.queryDelay = Math.max(this.queryDelay || 10,
18161                 this.mode == 'local' ? 10 : 250);
18162         
18163         
18164         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18165         
18166         if(this.typeAhead){
18167             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18168         }
18169         
18170         if(this.editable !== false){
18171             this.tickableInputEl().on("keyup", this.onKeyUp, this);
18172         }
18173         
18174         this.indicator = this.indicatorEl();
18175         
18176         if(this.indicator){
18177             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
18178             this.indicator.hide();
18179         }
18180         
18181     },
18182
18183     onDestroy : function(){
18184         if(this.view){
18185             this.view.setStore(null);
18186             this.view.el.removeAllListeners();
18187             this.view.el.remove();
18188             this.view.purgeListeners();
18189         }
18190         if(this.list){
18191             this.list.dom.innerHTML  = '';
18192         }
18193         
18194         if(this.store){
18195             this.store.un('beforeload', this.onBeforeLoad, this);
18196             this.store.un('load', this.onLoad, this);
18197             this.store.un('loadexception', this.onLoadException, this);
18198         }
18199         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
18200     },
18201
18202     // private
18203     fireKey : function(e){
18204         if(e.isNavKeyPress() && !this.list.isVisible()){
18205             this.fireEvent("specialkey", this, e);
18206         }
18207     },
18208
18209     // private
18210     onResize: function(w, h)
18211     {
18212         
18213         
18214 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
18215 //        
18216 //        if(typeof w != 'number'){
18217 //            // we do not handle it!?!?
18218 //            return;
18219 //        }
18220 //        var tw = this.trigger.getWidth();
18221 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
18222 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
18223 //        var x = w - tw;
18224 //        this.inputEl().setWidth( this.adjustWidth('input', x));
18225 //            
18226 //        //this.trigger.setStyle('left', x+'px');
18227 //        
18228 //        if(this.list && this.listWidth === undefined){
18229 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18230 //            this.list.setWidth(lw);
18231 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18232 //        }
18233         
18234     
18235         
18236     },
18237
18238     /**
18239      * Allow or prevent the user from directly editing the field text.  If false is passed,
18240      * the user will only be able to select from the items defined in the dropdown list.  This method
18241      * is the runtime equivalent of setting the 'editable' config option at config time.
18242      * @param {Boolean} value True to allow the user to directly edit the field text
18243      */
18244     setEditable : function(value){
18245         if(value == this.editable){
18246             return;
18247         }
18248         this.editable = value;
18249         if(!value){
18250             this.inputEl().dom.setAttribute('readOnly', true);
18251             this.inputEl().on('mousedown', this.onTriggerClick,  this);
18252             this.inputEl().addClass('x-combo-noedit');
18253         }else{
18254             this.inputEl().dom.removeAttribute('readOnly');
18255             this.inputEl().un('mousedown', this.onTriggerClick,  this);
18256             this.inputEl().removeClass('x-combo-noedit');
18257         }
18258     },
18259
18260     // private
18261     
18262     onBeforeLoad : function(combo,opts){
18263         if(!this.hasFocus){
18264             return;
18265         }
18266          if (!opts.add) {
18267             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18268          }
18269         this.restrictHeight();
18270         this.selectedIndex = -1;
18271     },
18272
18273     // private
18274     onLoad : function(){
18275         
18276         this.hasQuery = false;
18277         
18278         if(!this.hasFocus){
18279             return;
18280         }
18281         
18282         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18283             this.loading.hide();
18284         }
18285         
18286         if(this.store.getCount() > 0){
18287             
18288             this.expand();
18289             this.restrictHeight();
18290             if(this.lastQuery == this.allQuery){
18291                 if(this.editable && !this.tickable){
18292                     this.inputEl().dom.select();
18293                 }
18294                 
18295                 if(
18296                     !this.selectByValue(this.value, true) &&
18297                     this.autoFocus && 
18298                     (
18299                         !this.store.lastOptions ||
18300                         typeof(this.store.lastOptions.add) == 'undefined' || 
18301                         this.store.lastOptions.add != true
18302                     )
18303                 ){
18304                     this.select(0, true);
18305                 }
18306             }else{
18307                 if(this.autoFocus){
18308                     this.selectNext();
18309                 }
18310                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18311                     this.taTask.delay(this.typeAheadDelay);
18312                 }
18313             }
18314         }else{
18315             this.onEmptyResults();
18316         }
18317         
18318         //this.el.focus();
18319     },
18320     // private
18321     onLoadException : function()
18322     {
18323         this.hasQuery = false;
18324         
18325         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18326             this.loading.hide();
18327         }
18328         
18329         if(this.tickable && this.editable){
18330             return;
18331         }
18332         
18333         this.collapse();
18334         // only causes errors at present
18335         //Roo.log(this.store.reader.jsonData);
18336         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18337             // fixme
18338             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18339         //}
18340         
18341         
18342     },
18343     // private
18344     onTypeAhead : function(){
18345         if(this.store.getCount() > 0){
18346             var r = this.store.getAt(0);
18347             var newValue = r.data[this.displayField];
18348             var len = newValue.length;
18349             var selStart = this.getRawValue().length;
18350             
18351             if(selStart != len){
18352                 this.setRawValue(newValue);
18353                 this.selectText(selStart, newValue.length);
18354             }
18355         }
18356     },
18357
18358     // private
18359     onSelect : function(record, index){
18360         
18361         if(this.fireEvent('beforeselect', this, record, index) !== false){
18362         
18363             this.setFromData(index > -1 ? record.data : false);
18364             
18365             this.collapse();
18366             this.fireEvent('select', this, record, index);
18367         }
18368     },
18369
18370     /**
18371      * Returns the currently selected field value or empty string if no value is set.
18372      * @return {String} value The selected value
18373      */
18374     getValue : function()
18375     {
18376         if(Roo.isIOS && this.useNativeIOS){
18377             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18378         }
18379         
18380         if(this.multiple){
18381             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18382         }
18383         
18384         if(this.valueField){
18385             return typeof this.value != 'undefined' ? this.value : '';
18386         }else{
18387             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18388         }
18389     },
18390     
18391     getRawValue : function()
18392     {
18393         if(Roo.isIOS && this.useNativeIOS){
18394             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18395         }
18396         
18397         var v = this.inputEl().getValue();
18398         
18399         return v;
18400     },
18401
18402     /**
18403      * Clears any text/value currently set in the field
18404      */
18405     clearValue : function(){
18406         
18407         if(this.hiddenField){
18408             this.hiddenField.dom.value = '';
18409         }
18410         this.value = '';
18411         this.setRawValue('');
18412         this.lastSelectionText = '';
18413         this.lastData = false;
18414         
18415         var close = this.closeTriggerEl();
18416         
18417         if(close){
18418             close.hide();
18419         }
18420         
18421         this.validate();
18422         
18423     },
18424
18425     /**
18426      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18427      * will be displayed in the field.  If the value does not match the data value of an existing item,
18428      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18429      * Otherwise the field will be blank (although the value will still be set).
18430      * @param {String} value The value to match
18431      */
18432     setValue : function(v)
18433     {
18434         if(Roo.isIOS && this.useNativeIOS){
18435             this.setIOSValue(v);
18436             return;
18437         }
18438         
18439         if(this.multiple){
18440             this.syncValue();
18441             return;
18442         }
18443         
18444         var text = v;
18445         if(this.valueField){
18446             var r = this.findRecord(this.valueField, v);
18447             if(r){
18448                 text = r.data[this.displayField];
18449             }else if(this.valueNotFoundText !== undefined){
18450                 text = this.valueNotFoundText;
18451             }
18452         }
18453         this.lastSelectionText = text;
18454         if(this.hiddenField){
18455             this.hiddenField.dom.value = v;
18456         }
18457         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18458         this.value = v;
18459         
18460         var close = this.closeTriggerEl();
18461         
18462         if(close){
18463             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18464         }
18465         
18466         this.validate();
18467     },
18468     /**
18469      * @property {Object} the last set data for the element
18470      */
18471     
18472     lastData : false,
18473     /**
18474      * Sets the value of the field based on a object which is related to the record format for the store.
18475      * @param {Object} value the value to set as. or false on reset?
18476      */
18477     setFromData : function(o){
18478         
18479         if(this.multiple){
18480             this.addItem(o);
18481             return;
18482         }
18483             
18484         var dv = ''; // display value
18485         var vv = ''; // value value..
18486         this.lastData = o;
18487         if (this.displayField) {
18488             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18489         } else {
18490             // this is an error condition!!!
18491             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18492         }
18493         
18494         if(this.valueField){
18495             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18496         }
18497         
18498         var close = this.closeTriggerEl();
18499         
18500         if(close){
18501             if(dv.length || vv * 1 > 0){
18502                 close.show() ;
18503                 this.blockFocus=true;
18504             } else {
18505                 close.hide();
18506             }             
18507         }
18508         
18509         if(this.hiddenField){
18510             this.hiddenField.dom.value = vv;
18511             
18512             this.lastSelectionText = dv;
18513             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18514             this.value = vv;
18515             return;
18516         }
18517         // no hidden field.. - we store the value in 'value', but still display
18518         // display field!!!!
18519         this.lastSelectionText = dv;
18520         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18521         this.value = vv;
18522         
18523         
18524         
18525     },
18526     // private
18527     reset : function(){
18528         // overridden so that last data is reset..
18529         
18530         if(this.multiple){
18531             this.clearItem();
18532             return;
18533         }
18534         
18535         this.setValue(this.originalValue);
18536         //this.clearInvalid();
18537         this.lastData = false;
18538         if (this.view) {
18539             this.view.clearSelections();
18540         }
18541         
18542         this.validate();
18543     },
18544     // private
18545     findRecord : function(prop, value){
18546         var record;
18547         if(this.store.getCount() > 0){
18548             this.store.each(function(r){
18549                 if(r.data[prop] == value){
18550                     record = r;
18551                     return false;
18552                 }
18553                 return true;
18554             });
18555         }
18556         return record;
18557     },
18558     
18559     getName: function()
18560     {
18561         // returns hidden if it's set..
18562         if (!this.rendered) {return ''};
18563         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18564         
18565     },
18566     // private
18567     onViewMove : function(e, t){
18568         this.inKeyMode = false;
18569     },
18570
18571     // private
18572     onViewOver : function(e, t){
18573         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18574             return;
18575         }
18576         var item = this.view.findItemFromChild(t);
18577         
18578         if(item){
18579             var index = this.view.indexOf(item);
18580             this.select(index, false);
18581         }
18582     },
18583
18584     // private
18585     onViewClick : function(view, doFocus, el, e)
18586     {
18587         var index = this.view.getSelectedIndexes()[0];
18588         
18589         var r = this.store.getAt(index);
18590         
18591         if(this.tickable){
18592             
18593             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18594                 return;
18595             }
18596             
18597             var rm = false;
18598             var _this = this;
18599             
18600             Roo.each(this.tickItems, function(v,k){
18601                 
18602                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18603                     Roo.log(v);
18604                     _this.tickItems.splice(k, 1);
18605                     
18606                     if(typeof(e) == 'undefined' && view == false){
18607                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18608                     }
18609                     
18610                     rm = true;
18611                     return;
18612                 }
18613             });
18614             
18615             if(rm){
18616                 return;
18617             }
18618             
18619             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18620                 this.tickItems.push(r.data);
18621             }
18622             
18623             if(typeof(e) == 'undefined' && view == false){
18624                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18625             }
18626                     
18627             return;
18628         }
18629         
18630         if(r){
18631             this.onSelect(r, index);
18632         }
18633         if(doFocus !== false && !this.blockFocus){
18634             this.inputEl().focus();
18635         }
18636     },
18637
18638     // private
18639     restrictHeight : function(){
18640         //this.innerList.dom.style.height = '';
18641         //var inner = this.innerList.dom;
18642         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18643         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18644         //this.list.beginUpdate();
18645         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18646         this.list.alignTo(this.inputEl(), this.listAlign);
18647         this.list.alignTo(this.inputEl(), this.listAlign);
18648         //this.list.endUpdate();
18649     },
18650
18651     // private
18652     onEmptyResults : function(){
18653         
18654         if(this.tickable && this.editable){
18655             this.hasFocus = false;
18656             this.restrictHeight();
18657             return;
18658         }
18659         
18660         this.collapse();
18661     },
18662
18663     /**
18664      * Returns true if the dropdown list is expanded, else false.
18665      */
18666     isExpanded : function(){
18667         return this.list.isVisible();
18668     },
18669
18670     /**
18671      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18672      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18673      * @param {String} value The data value of the item to select
18674      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18675      * selected item if it is not currently in view (defaults to true)
18676      * @return {Boolean} True if the value matched an item in the list, else false
18677      */
18678     selectByValue : function(v, scrollIntoView){
18679         if(v !== undefined && v !== null){
18680             var r = this.findRecord(this.valueField || this.displayField, v);
18681             if(r){
18682                 this.select(this.store.indexOf(r), scrollIntoView);
18683                 return true;
18684             }
18685         }
18686         return false;
18687     },
18688
18689     /**
18690      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18691      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18692      * @param {Number} index The zero-based index of the list item to select
18693      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18694      * selected item if it is not currently in view (defaults to true)
18695      */
18696     select : function(index, scrollIntoView){
18697         this.selectedIndex = index;
18698         this.view.select(index);
18699         if(scrollIntoView !== false){
18700             var el = this.view.getNode(index);
18701             /*
18702              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18703              */
18704             if(el){
18705                 this.list.scrollChildIntoView(el, false);
18706             }
18707         }
18708     },
18709
18710     // private
18711     selectNext : function(){
18712         var ct = this.store.getCount();
18713         if(ct > 0){
18714             if(this.selectedIndex == -1){
18715                 this.select(0);
18716             }else if(this.selectedIndex < ct-1){
18717                 this.select(this.selectedIndex+1);
18718             }
18719         }
18720     },
18721
18722     // private
18723     selectPrev : function(){
18724         var ct = this.store.getCount();
18725         if(ct > 0){
18726             if(this.selectedIndex == -1){
18727                 this.select(0);
18728             }else if(this.selectedIndex != 0){
18729                 this.select(this.selectedIndex-1);
18730             }
18731         }
18732     },
18733
18734     // private
18735     onKeyUp : function(e){
18736         if(this.editable !== false && !e.isSpecialKey()){
18737             this.lastKey = e.getKey();
18738             this.dqTask.delay(this.queryDelay);
18739         }
18740     },
18741
18742     // private
18743     validateBlur : function(){
18744         return !this.list || !this.list.isVisible();   
18745     },
18746
18747     // private
18748     initQuery : function(){
18749         
18750         var v = this.getRawValue();
18751         
18752         if(this.tickable && this.editable){
18753             v = this.tickableInputEl().getValue();
18754         }
18755         
18756         this.doQuery(v);
18757     },
18758
18759     // private
18760     doForce : function(){
18761         if(this.inputEl().dom.value.length > 0){
18762             this.inputEl().dom.value =
18763                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18764              
18765         }
18766     },
18767
18768     /**
18769      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18770      * query allowing the query action to be canceled if needed.
18771      * @param {String} query The SQL query to execute
18772      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18773      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18774      * saved in the current store (defaults to false)
18775      */
18776     doQuery : function(q, forceAll){
18777         
18778         if(q === undefined || q === null){
18779             q = '';
18780         }
18781         var qe = {
18782             query: q,
18783             forceAll: forceAll,
18784             combo: this,
18785             cancel:false
18786         };
18787         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18788             return false;
18789         }
18790         q = qe.query;
18791         
18792         forceAll = qe.forceAll;
18793         if(forceAll === true || (q.length >= this.minChars)){
18794             
18795             this.hasQuery = true;
18796             
18797             if(this.lastQuery != q || this.alwaysQuery){
18798                 this.lastQuery = q;
18799                 if(this.mode == 'local'){
18800                     this.selectedIndex = -1;
18801                     if(forceAll){
18802                         this.store.clearFilter();
18803                     }else{
18804                         
18805                         if(this.specialFilter){
18806                             this.fireEvent('specialfilter', this);
18807                             this.onLoad();
18808                             return;
18809                         }
18810                         
18811                         this.store.filter(this.displayField, q);
18812                     }
18813                     
18814                     this.store.fireEvent("datachanged", this.store);
18815                     
18816                     this.onLoad();
18817                     
18818                     
18819                 }else{
18820                     
18821                     this.store.baseParams[this.queryParam] = q;
18822                     
18823                     var options = {params : this.getParams(q)};
18824                     
18825                     if(this.loadNext){
18826                         options.add = true;
18827                         options.params.start = this.page * this.pageSize;
18828                     }
18829                     
18830                     this.store.load(options);
18831                     
18832                     /*
18833                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18834                      *  we should expand the list on onLoad
18835                      *  so command out it
18836                      */
18837 //                    this.expand();
18838                 }
18839             }else{
18840                 this.selectedIndex = -1;
18841                 this.onLoad();   
18842             }
18843         }
18844         
18845         this.loadNext = false;
18846     },
18847     
18848     // private
18849     getParams : function(q){
18850         var p = {};
18851         //p[this.queryParam] = q;
18852         
18853         if(this.pageSize){
18854             p.start = 0;
18855             p.limit = this.pageSize;
18856         }
18857         return p;
18858     },
18859
18860     /**
18861      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18862      */
18863     collapse : function(){
18864         if(!this.isExpanded()){
18865             return;
18866         }
18867         
18868         this.list.hide();
18869         
18870         this.hasFocus = false;
18871         
18872         if(this.tickable){
18873             this.okBtn.hide();
18874             this.cancelBtn.hide();
18875             this.trigger.show();
18876             
18877             if(this.editable){
18878                 this.tickableInputEl().dom.value = '';
18879                 this.tickableInputEl().blur();
18880             }
18881             
18882         }
18883         
18884         Roo.get(document).un('mousedown', this.collapseIf, this);
18885         Roo.get(document).un('mousewheel', this.collapseIf, this);
18886         if (!this.editable) {
18887             Roo.get(document).un('keydown', this.listKeyPress, this);
18888         }
18889         this.fireEvent('collapse', this);
18890         
18891         this.validate();
18892     },
18893
18894     // private
18895     collapseIf : function(e){
18896         var in_combo  = e.within(this.el);
18897         var in_list =  e.within(this.list);
18898         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18899         
18900         if (in_combo || in_list || is_list) {
18901             //e.stopPropagation();
18902             return;
18903         }
18904         
18905         if(this.tickable){
18906             this.onTickableFooterButtonClick(e, false, false);
18907         }
18908
18909         this.collapse();
18910         
18911     },
18912
18913     /**
18914      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18915      */
18916     expand : function(){
18917        
18918         if(this.isExpanded() || !this.hasFocus){
18919             return;
18920         }
18921         
18922         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18923         this.list.setWidth(lw);
18924         
18925         Roo.log('expand');
18926         
18927         this.list.show();
18928         
18929         this.restrictHeight();
18930         
18931         if(this.tickable){
18932             
18933             this.tickItems = Roo.apply([], this.item);
18934             
18935             this.okBtn.show();
18936             this.cancelBtn.show();
18937             this.trigger.hide();
18938             
18939             if(this.editable){
18940                 this.tickableInputEl().focus();
18941             }
18942             
18943         }
18944         
18945         Roo.get(document).on('mousedown', this.collapseIf, this);
18946         Roo.get(document).on('mousewheel', this.collapseIf, this);
18947         if (!this.editable) {
18948             Roo.get(document).on('keydown', this.listKeyPress, this);
18949         }
18950
18951         this.list.setStyle('maxHeight', 'calc(100% - ' + this.list.getTop() + 'px - 50px)');
18952         
18953         this.fireEvent('expand', this);
18954     },
18955
18956     // private
18957     // Implements the default empty TriggerField.onTriggerClick function
18958     onTriggerClick : function(e)
18959     {
18960         Roo.log('trigger click');
18961         
18962         if(this.disabled || !this.triggerList){
18963             return;
18964         }
18965         
18966         this.page = 0;
18967         this.loadNext = false;
18968         
18969         if(this.isExpanded()){
18970             this.collapse();
18971             if (!this.blockFocus) {
18972                 this.inputEl().focus();
18973             }
18974             
18975         }else {
18976             this.hasFocus = true;
18977             if(this.triggerAction == 'all') {
18978                 this.doQuery(this.allQuery, true);
18979             } else {
18980                 this.doQuery(this.getRawValue());
18981             }
18982             if (!this.blockFocus) {
18983                 this.inputEl().focus();
18984             }
18985         }
18986     },
18987     
18988     onTickableTriggerClick : function(e)
18989     {
18990         if(this.disabled){
18991             return;
18992         }
18993         
18994         this.page = 0;
18995         this.loadNext = false;
18996         this.hasFocus = true;
18997         
18998         if(this.triggerAction == 'all') {
18999             this.doQuery(this.allQuery, true);
19000         } else {
19001             this.doQuery(this.getRawValue());
19002         }
19003     },
19004     
19005     onSearchFieldClick : function(e)
19006     {
19007         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
19008             this.onTickableFooterButtonClick(e, false, false);
19009             return;
19010         }
19011         
19012         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
19013             return;
19014         }
19015         
19016         this.page = 0;
19017         this.loadNext = false;
19018         this.hasFocus = true;
19019         
19020         if(this.triggerAction == 'all') {
19021             this.doQuery(this.allQuery, true);
19022         } else {
19023             this.doQuery(this.getRawValue());
19024         }
19025     },
19026     
19027     listKeyPress : function(e)
19028     {
19029         //Roo.log('listkeypress');
19030         // scroll to first matching element based on key pres..
19031         if (e.isSpecialKey()) {
19032             return false;
19033         }
19034         var k = String.fromCharCode(e.getKey()).toUpperCase();
19035         //Roo.log(k);
19036         var match  = false;
19037         var csel = this.view.getSelectedNodes();
19038         var cselitem = false;
19039         if (csel.length) {
19040             var ix = this.view.indexOf(csel[0]);
19041             cselitem  = this.store.getAt(ix);
19042             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19043                 cselitem = false;
19044             }
19045             
19046         }
19047         
19048         this.store.each(function(v) { 
19049             if (cselitem) {
19050                 // start at existing selection.
19051                 if (cselitem.id == v.id) {
19052                     cselitem = false;
19053                 }
19054                 return true;
19055             }
19056                 
19057             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19058                 match = this.store.indexOf(v);
19059                 return false;
19060             }
19061             return true;
19062         }, this);
19063         
19064         if (match === false) {
19065             return true; // no more action?
19066         }
19067         // scroll to?
19068         this.view.select(match);
19069         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19070         sn.scrollIntoView(sn.dom.parentNode, false);
19071     },
19072     
19073     onViewScroll : function(e, t){
19074         
19075         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){
19076             return;
19077         }
19078         
19079         this.hasQuery = true;
19080         
19081         this.loading = this.list.select('.loading', true).first();
19082         
19083         if(this.loading === null){
19084             this.list.createChild({
19085                 tag: 'div',
19086                 cls: 'loading roo-select2-more-results roo-select2-active',
19087                 html: 'Loading more results...'
19088             });
19089             
19090             this.loading = this.list.select('.loading', true).first();
19091             
19092             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
19093             
19094             this.loading.hide();
19095         }
19096         
19097         this.loading.show();
19098         
19099         var _combo = this;
19100         
19101         this.page++;
19102         this.loadNext = true;
19103         
19104         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
19105         
19106         return;
19107     },
19108     
19109     addItem : function(o)
19110     {   
19111         var dv = ''; // display value
19112         
19113         if (this.displayField) {
19114             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19115         } else {
19116             // this is an error condition!!!
19117             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19118         }
19119         
19120         if(!dv.length){
19121             return;
19122         }
19123         
19124         var choice = this.choices.createChild({
19125             tag: 'li',
19126             cls: 'roo-select2-search-choice',
19127             cn: [
19128                 {
19129                     tag: 'div',
19130                     html: dv
19131                 },
19132                 {
19133                     tag: 'a',
19134                     href: '#',
19135                     cls: 'roo-select2-search-choice-close fa fa-times',
19136                     tabindex: '-1'
19137                 }
19138             ]
19139             
19140         }, this.searchField);
19141         
19142         var close = choice.select('a.roo-select2-search-choice-close', true).first();
19143         
19144         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
19145         
19146         this.item.push(o);
19147         
19148         this.lastData = o;
19149         
19150         this.syncValue();
19151         
19152         this.inputEl().dom.value = '';
19153         
19154         this.validate();
19155     },
19156     
19157     onRemoveItem : function(e, _self, o)
19158     {
19159         e.preventDefault();
19160         
19161         this.lastItem = Roo.apply([], this.item);
19162         
19163         var index = this.item.indexOf(o.data) * 1;
19164         
19165         if( index < 0){
19166             Roo.log('not this item?!');
19167             return;
19168         }
19169         
19170         this.item.splice(index, 1);
19171         o.item.remove();
19172         
19173         this.syncValue();
19174         
19175         this.fireEvent('remove', this, e);
19176         
19177         this.validate();
19178         
19179     },
19180     
19181     syncValue : function()
19182     {
19183         if(!this.item.length){
19184             this.clearValue();
19185             return;
19186         }
19187             
19188         var value = [];
19189         var _this = this;
19190         Roo.each(this.item, function(i){
19191             if(_this.valueField){
19192                 value.push(i[_this.valueField]);
19193                 return;
19194             }
19195
19196             value.push(i);
19197         });
19198
19199         this.value = value.join(',');
19200
19201         if(this.hiddenField){
19202             this.hiddenField.dom.value = this.value;
19203         }
19204         
19205         this.store.fireEvent("datachanged", this.store);
19206         
19207         this.validate();
19208     },
19209     
19210     clearItem : function()
19211     {
19212         if(!this.multiple){
19213             return;
19214         }
19215         
19216         this.item = [];
19217         
19218         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
19219            c.remove();
19220         });
19221         
19222         this.syncValue();
19223         
19224         this.validate();
19225         
19226         if(this.tickable && !Roo.isTouch){
19227             this.view.refresh();
19228         }
19229     },
19230     
19231     inputEl: function ()
19232     {
19233         if(Roo.isIOS && this.useNativeIOS){
19234             return this.el.select('select.roo-ios-select', true).first();
19235         }
19236         
19237         if(Roo.isTouch && this.mobileTouchView){
19238             return this.el.select('input.form-control',true).first();
19239         }
19240         
19241         if(this.tickable){
19242             return this.searchField;
19243         }
19244         
19245         return this.el.select('input.form-control',true).first();
19246     },
19247     
19248     onTickableFooterButtonClick : function(e, btn, el)
19249     {
19250         e.preventDefault();
19251         
19252         this.lastItem = Roo.apply([], this.item);
19253         
19254         if(btn && btn.name == 'cancel'){
19255             this.tickItems = Roo.apply([], this.item);
19256             this.collapse();
19257             return;
19258         }
19259         
19260         this.clearItem();
19261         
19262         var _this = this;
19263         
19264         Roo.each(this.tickItems, function(o){
19265             _this.addItem(o);
19266         });
19267         
19268         this.collapse();
19269         
19270     },
19271     
19272     validate : function()
19273     {
19274         if(this.getVisibilityEl().hasClass('hidden')){
19275             return true;
19276         }
19277         
19278         var v = this.getRawValue();
19279         
19280         if(this.multiple){
19281             v = this.getValue();
19282         }
19283         
19284         if(this.disabled || this.allowBlank || v.length){
19285             this.markValid();
19286             return true;
19287         }
19288         
19289         this.markInvalid();
19290         return false;
19291     },
19292     
19293     tickableInputEl : function()
19294     {
19295         if(!this.tickable || !this.editable){
19296             return this.inputEl();
19297         }
19298         
19299         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19300     },
19301     
19302     
19303     getAutoCreateTouchView : function()
19304     {
19305         var id = Roo.id();
19306         
19307         var cfg = {
19308             cls: 'form-group' //input-group
19309         };
19310         
19311         var input =  {
19312             tag: 'input',
19313             id : id,
19314             type : this.inputType,
19315             cls : 'form-control x-combo-noedit',
19316             autocomplete: 'new-password',
19317             placeholder : this.placeholder || '',
19318             readonly : true
19319         };
19320         
19321         if (this.name) {
19322             input.name = this.name;
19323         }
19324         
19325         if (this.size) {
19326             input.cls += ' input-' + this.size;
19327         }
19328         
19329         if (this.disabled) {
19330             input.disabled = true;
19331         }
19332         
19333         var inputblock = {
19334             cls : 'roo-combobox-wrap',
19335             cn : [
19336                 input
19337             ]
19338         };
19339         
19340         if(this.before){
19341             inputblock.cls += ' input-group';
19342             
19343             inputblock.cn.unshift({
19344                 tag :'span',
19345                 cls : 'input-group-addon input-group-prepend input-group-text',
19346                 html : this.before
19347             });
19348         }
19349         
19350         if(this.removable && !this.multiple){
19351             inputblock.cls += ' roo-removable';
19352             
19353             inputblock.cn.push({
19354                 tag: 'button',
19355                 html : 'x',
19356                 cls : 'roo-combo-removable-btn close'
19357             });
19358         }
19359
19360         if(this.hasFeedback && !this.allowBlank){
19361             
19362             inputblock.cls += ' has-feedback';
19363             
19364             inputblock.cn.push({
19365                 tag: 'span',
19366                 cls: 'glyphicon form-control-feedback'
19367             });
19368             
19369         }
19370         
19371         if (this.after) {
19372             
19373             inputblock.cls += (this.before) ? '' : ' input-group';
19374             
19375             inputblock.cn.push({
19376                 tag :'span',
19377                 cls : 'input-group-addon input-group-append input-group-text',
19378                 html : this.after
19379             });
19380         }
19381
19382         
19383         var ibwrap = inputblock;
19384         
19385         if(this.multiple){
19386             ibwrap = {
19387                 tag: 'ul',
19388                 cls: 'roo-select2-choices',
19389                 cn:[
19390                     {
19391                         tag: 'li',
19392                         cls: 'roo-select2-search-field',
19393                         cn: [
19394
19395                             inputblock
19396                         ]
19397                     }
19398                 ]
19399             };
19400         
19401             
19402         }
19403         
19404         var combobox = {
19405             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19406             cn: [
19407                 {
19408                     tag: 'input',
19409                     type : 'hidden',
19410                     cls: 'form-hidden-field'
19411                 },
19412                 ibwrap
19413             ]
19414         };
19415         
19416         if(!this.multiple && this.showToggleBtn){
19417             
19418             var caret = {
19419                 cls: 'caret'
19420             };
19421             
19422             if (this.caret != false) {
19423                 caret = {
19424                      tag: 'i',
19425                      cls: 'fa fa-' + this.caret
19426                 };
19427                 
19428             }
19429             
19430             combobox.cn.push({
19431                 tag :'span',
19432                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19433                 cn : [
19434                     Roo.bootstrap.version == 3 ? caret : '',
19435                     {
19436                         tag: 'span',
19437                         cls: 'combobox-clear',
19438                         cn  : [
19439                             {
19440                                 tag : 'i',
19441                                 cls: 'icon-remove'
19442                             }
19443                         ]
19444                     }
19445                 ]
19446
19447             })
19448         }
19449         
19450         if(this.multiple){
19451             combobox.cls += ' roo-select2-container-multi';
19452         }
19453         
19454         var required =  this.allowBlank ?  {
19455                     tag : 'i',
19456                     style: 'display: none'
19457                 } : {
19458                    tag : 'i',
19459                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19460                    tooltip : 'This field is required'
19461                 };
19462         
19463         var align = this.labelAlign || this.parentLabelAlign();
19464         
19465         if (align ==='left' && this.fieldLabel.length) {
19466
19467             cfg.cn = [
19468                 required,
19469                 {
19470                     tag: 'label',
19471                     cls : 'control-label col-form-label',
19472                     html : this.fieldLabel
19473
19474                 },
19475                 {
19476                     cls : 'roo-combobox-wrap ', 
19477                     cn: [
19478                         combobox
19479                     ]
19480                 }
19481             ];
19482             
19483             var labelCfg = cfg.cn[1];
19484             var contentCfg = cfg.cn[2];
19485             
19486
19487             if(this.indicatorpos == 'right'){
19488                 cfg.cn = [
19489                     {
19490                         tag: 'label',
19491                         'for' :  id,
19492                         cls : 'control-label col-form-label',
19493                         cn : [
19494                             {
19495                                 tag : 'span',
19496                                 html : this.fieldLabel
19497                             },
19498                             required
19499                         ]
19500                     },
19501                     {
19502                         cls : "roo-combobox-wrap ",
19503                         cn: [
19504                             combobox
19505                         ]
19506                     }
19507
19508                 ];
19509                 
19510                 labelCfg = cfg.cn[0];
19511                 contentCfg = cfg.cn[1];
19512             }
19513             
19514            
19515             
19516             if(this.labelWidth > 12){
19517                 labelCfg.style = "width: " + this.labelWidth + 'px';
19518             }
19519            
19520             if(this.labelWidth < 13 && this.labelmd == 0){
19521                 this.labelmd = this.labelWidth;
19522             }
19523             
19524             if(this.labellg > 0){
19525                 labelCfg.cls += ' col-lg-' + this.labellg;
19526                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19527             }
19528             
19529             if(this.labelmd > 0){
19530                 labelCfg.cls += ' col-md-' + this.labelmd;
19531                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19532             }
19533             
19534             if(this.labelsm > 0){
19535                 labelCfg.cls += ' col-sm-' + this.labelsm;
19536                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19537             }
19538             
19539             if(this.labelxs > 0){
19540                 labelCfg.cls += ' col-xs-' + this.labelxs;
19541                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19542             }
19543                 
19544                 
19545         } else if ( this.fieldLabel.length) {
19546             cfg.cn = [
19547                required,
19548                 {
19549                     tag: 'label',
19550                     cls : 'control-label',
19551                     html : this.fieldLabel
19552
19553                 },
19554                 {
19555                     cls : '', 
19556                     cn: [
19557                         combobox
19558                     ]
19559                 }
19560             ];
19561             
19562             if(this.indicatorpos == 'right'){
19563                 cfg.cn = [
19564                     {
19565                         tag: 'label',
19566                         cls : 'control-label',
19567                         html : this.fieldLabel,
19568                         cn : [
19569                             required
19570                         ]
19571                     },
19572                     {
19573                         cls : '', 
19574                         cn: [
19575                             combobox
19576                         ]
19577                     }
19578                 ];
19579             }
19580         } else {
19581             cfg.cn = combobox;    
19582         }
19583         
19584         
19585         var settings = this;
19586         
19587         ['xs','sm','md','lg'].map(function(size){
19588             if (settings[size]) {
19589                 cfg.cls += ' col-' + size + '-' + settings[size];
19590             }
19591         });
19592         
19593         return cfg;
19594     },
19595     
19596     initTouchView : function()
19597     {
19598         this.renderTouchView();
19599         
19600         this.touchViewEl.on('scroll', function(){
19601             this.el.dom.scrollTop = 0;
19602         }, this);
19603         
19604         this.originalValue = this.getValue();
19605         
19606         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19607         
19608         this.inputEl().on("click", this.showTouchView, this);
19609         if (this.triggerEl) {
19610             this.triggerEl.on("click", this.showTouchView, this);
19611         }
19612         
19613         
19614         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19615         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19616         
19617         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19618         
19619         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19620         this.store.on('load', this.onTouchViewLoad, this);
19621         this.store.on('loadexception', this.onTouchViewLoadException, this);
19622         
19623         if(this.hiddenName){
19624             
19625             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19626             
19627             this.hiddenField.dom.value =
19628                 this.hiddenValue !== undefined ? this.hiddenValue :
19629                 this.value !== undefined ? this.value : '';
19630         
19631             this.el.dom.removeAttribute('name');
19632             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19633         }
19634         
19635         if(this.multiple){
19636             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19637             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19638         }
19639         
19640         if(this.removable && !this.multiple){
19641             var close = this.closeTriggerEl();
19642             if(close){
19643                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19644                 close.on('click', this.removeBtnClick, this, close);
19645             }
19646         }
19647         /*
19648          * fix the bug in Safari iOS8
19649          */
19650         this.inputEl().on("focus", function(e){
19651             document.activeElement.blur();
19652         }, this);
19653         
19654         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19655         
19656         return;
19657         
19658         
19659     },
19660     
19661     renderTouchView : function()
19662     {
19663         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19664         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19665         
19666         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19667         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19668         
19669         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19670         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19671         this.touchViewBodyEl.setStyle('overflow', 'auto');
19672         
19673         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19674         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19675         
19676         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19677         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19678         
19679     },
19680     
19681     showTouchView : function()
19682     {
19683         if(this.disabled){
19684             return;
19685         }
19686         
19687         this.touchViewHeaderEl.hide();
19688
19689         if(this.modalTitle.length){
19690             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19691             this.touchViewHeaderEl.show();
19692         }
19693
19694         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19695         this.touchViewEl.show();
19696
19697         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19698         
19699         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19700         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19701
19702         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19703
19704         if(this.modalTitle.length){
19705             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19706         }
19707         
19708         this.touchViewBodyEl.setHeight(bodyHeight);
19709
19710         if(this.animate){
19711             var _this = this;
19712             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19713         }else{
19714             this.touchViewEl.addClass(['in','show']);
19715         }
19716         
19717         if(this._touchViewMask){
19718             Roo.get(document.body).addClass("x-body-masked");
19719             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19720             this._touchViewMask.setStyle('z-index', 10000);
19721             this._touchViewMask.addClass('show');
19722         }
19723         
19724         this.doTouchViewQuery();
19725         
19726     },
19727     
19728     hideTouchView : function()
19729     {
19730         this.touchViewEl.removeClass(['in','show']);
19731
19732         if(this.animate){
19733             var _this = this;
19734             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19735         }else{
19736             this.touchViewEl.setStyle('display', 'none');
19737         }
19738         
19739         if(this._touchViewMask){
19740             this._touchViewMask.removeClass('show');
19741             Roo.get(document.body).removeClass("x-body-masked");
19742         }
19743     },
19744     
19745     setTouchViewValue : function()
19746     {
19747         if(this.multiple){
19748             this.clearItem();
19749         
19750             var _this = this;
19751
19752             Roo.each(this.tickItems, function(o){
19753                 this.addItem(o);
19754             }, this);
19755         }
19756         
19757         this.hideTouchView();
19758     },
19759     
19760     doTouchViewQuery : function()
19761     {
19762         var qe = {
19763             query: '',
19764             forceAll: true,
19765             combo: this,
19766             cancel:false
19767         };
19768         
19769         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19770             return false;
19771         }
19772         
19773         if(!this.alwaysQuery || this.mode == 'local'){
19774             this.onTouchViewLoad();
19775             return;
19776         }
19777         
19778         this.store.load();
19779     },
19780     
19781     onTouchViewBeforeLoad : function(combo,opts)
19782     {
19783         return;
19784     },
19785
19786     // private
19787     onTouchViewLoad : function()
19788     {
19789         if(this.store.getCount() < 1){
19790             this.onTouchViewEmptyResults();
19791             return;
19792         }
19793         
19794         this.clearTouchView();
19795         
19796         var rawValue = this.getRawValue();
19797         
19798         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19799         
19800         this.tickItems = [];
19801         
19802         this.store.data.each(function(d, rowIndex){
19803             var row = this.touchViewListGroup.createChild(template);
19804             
19805             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19806                 row.addClass(d.data.cls);
19807             }
19808             
19809             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19810                 var cfg = {
19811                     data : d.data,
19812                     html : d.data[this.displayField]
19813                 };
19814                 
19815                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19816                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19817                 }
19818             }
19819             row.removeClass('selected');
19820             if(!this.multiple && this.valueField &&
19821                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19822             {
19823                 // radio buttons..
19824                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19825                 row.addClass('selected');
19826             }
19827             
19828             if(this.multiple && this.valueField &&
19829                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19830             {
19831                 
19832                 // checkboxes...
19833                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19834                 this.tickItems.push(d.data);
19835             }
19836             
19837             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19838             
19839         }, this);
19840         
19841         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19842         
19843         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19844
19845         if(this.modalTitle.length){
19846             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19847         }
19848
19849         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19850         
19851         if(this.mobile_restrict_height && listHeight < bodyHeight){
19852             this.touchViewBodyEl.setHeight(listHeight);
19853         }
19854         
19855         var _this = this;
19856         
19857         if(firstChecked && listHeight > bodyHeight){
19858             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19859         }
19860         
19861     },
19862     
19863     onTouchViewLoadException : function()
19864     {
19865         this.hideTouchView();
19866     },
19867     
19868     onTouchViewEmptyResults : function()
19869     {
19870         this.clearTouchView();
19871         
19872         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19873         
19874         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19875         
19876     },
19877     
19878     clearTouchView : function()
19879     {
19880         this.touchViewListGroup.dom.innerHTML = '';
19881     },
19882     
19883     onTouchViewClick : function(e, el, o)
19884     {
19885         e.preventDefault();
19886         
19887         var row = o.row;
19888         var rowIndex = o.rowIndex;
19889         
19890         var r = this.store.getAt(rowIndex);
19891         
19892         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19893             
19894             if(!this.multiple){
19895                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19896                     c.dom.removeAttribute('checked');
19897                 }, this);
19898
19899                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19900
19901                 this.setFromData(r.data);
19902
19903                 var close = this.closeTriggerEl();
19904
19905                 if(close){
19906                     close.show();
19907                 }
19908
19909                 this.hideTouchView();
19910
19911                 this.fireEvent('select', this, r, rowIndex);
19912
19913                 return;
19914             }
19915
19916             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19917                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19918                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19919                 return;
19920             }
19921
19922             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19923             this.addItem(r.data);
19924             this.tickItems.push(r.data);
19925         }
19926     },
19927     
19928     getAutoCreateNativeIOS : function()
19929     {
19930         var cfg = {
19931             cls: 'form-group' //input-group,
19932         };
19933         
19934         var combobox =  {
19935             tag: 'select',
19936             cls : 'roo-ios-select'
19937         };
19938         
19939         if (this.name) {
19940             combobox.name = this.name;
19941         }
19942         
19943         if (this.disabled) {
19944             combobox.disabled = true;
19945         }
19946         
19947         var settings = this;
19948         
19949         ['xs','sm','md','lg'].map(function(size){
19950             if (settings[size]) {
19951                 cfg.cls += ' col-' + size + '-' + settings[size];
19952             }
19953         });
19954         
19955         cfg.cn = combobox;
19956         
19957         return cfg;
19958         
19959     },
19960     
19961     initIOSView : function()
19962     {
19963         this.store.on('load', this.onIOSViewLoad, this);
19964         
19965         return;
19966     },
19967     
19968     onIOSViewLoad : function()
19969     {
19970         if(this.store.getCount() < 1){
19971             return;
19972         }
19973         
19974         this.clearIOSView();
19975         
19976         if(this.allowBlank) {
19977             
19978             var default_text = '-- SELECT --';
19979             
19980             if(this.placeholder.length){
19981                 default_text = this.placeholder;
19982             }
19983             
19984             if(this.emptyTitle.length){
19985                 default_text += ' - ' + this.emptyTitle + ' -';
19986             }
19987             
19988             var opt = this.inputEl().createChild({
19989                 tag: 'option',
19990                 value : 0,
19991                 html : default_text
19992             });
19993             
19994             var o = {};
19995             o[this.valueField] = 0;
19996             o[this.displayField] = default_text;
19997             
19998             this.ios_options.push({
19999                 data : o,
20000                 el : opt
20001             });
20002             
20003         }
20004         
20005         this.store.data.each(function(d, rowIndex){
20006             
20007             var html = '';
20008             
20009             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
20010                 html = d.data[this.displayField];
20011             }
20012             
20013             var value = '';
20014             
20015             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
20016                 value = d.data[this.valueField];
20017             }
20018             
20019             var option = {
20020                 tag: 'option',
20021                 value : value,
20022                 html : html
20023             };
20024             
20025             if(this.value == d.data[this.valueField]){
20026                 option['selected'] = true;
20027             }
20028             
20029             var opt = this.inputEl().createChild(option);
20030             
20031             this.ios_options.push({
20032                 data : d.data,
20033                 el : opt
20034             });
20035             
20036         }, this);
20037         
20038         this.inputEl().on('change', function(){
20039            this.fireEvent('select', this);
20040         }, this);
20041         
20042     },
20043     
20044     clearIOSView: function()
20045     {
20046         this.inputEl().dom.innerHTML = '';
20047         
20048         this.ios_options = [];
20049     },
20050     
20051     setIOSValue: function(v)
20052     {
20053         this.value = v;
20054         
20055         if(!this.ios_options){
20056             return;
20057         }
20058         
20059         Roo.each(this.ios_options, function(opts){
20060            
20061            opts.el.dom.removeAttribute('selected');
20062            
20063            if(opts.data[this.valueField] != v){
20064                return;
20065            }
20066            
20067            opts.el.dom.setAttribute('selected', true);
20068            
20069         }, this);
20070     }
20071
20072     /** 
20073     * @cfg {Boolean} grow 
20074     * @hide 
20075     */
20076     /** 
20077     * @cfg {Number} growMin 
20078     * @hide 
20079     */
20080     /** 
20081     * @cfg {Number} growMax 
20082     * @hide 
20083     */
20084     /**
20085      * @hide
20086      * @method autoSize
20087      */
20088 });
20089
20090 Roo.apply(Roo.bootstrap.form.ComboBox,  {
20091     
20092     header : {
20093         tag: 'div',
20094         cls: 'modal-header',
20095         cn: [
20096             {
20097                 tag: 'h4',
20098                 cls: 'modal-title'
20099             }
20100         ]
20101     },
20102     
20103     body : {
20104         tag: 'div',
20105         cls: 'modal-body',
20106         cn: [
20107             {
20108                 tag: 'ul',
20109                 cls: 'list-group'
20110             }
20111         ]
20112     },
20113     
20114     listItemRadio : {
20115         tag: 'li',
20116         cls: 'list-group-item',
20117         cn: [
20118             {
20119                 tag: 'span',
20120                 cls: 'roo-combobox-list-group-item-value'
20121             },
20122             {
20123                 tag: 'div',
20124                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
20125                 cn: [
20126                     {
20127                         tag: 'input',
20128                         type: 'radio'
20129                     },
20130                     {
20131                         tag: 'label'
20132                     }
20133                 ]
20134             }
20135         ]
20136     },
20137     
20138     listItemCheckbox : {
20139         tag: 'li',
20140         cls: 'list-group-item',
20141         cn: [
20142             {
20143                 tag: 'span',
20144                 cls: 'roo-combobox-list-group-item-value'
20145             },
20146             {
20147                 tag: 'div',
20148                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
20149                 cn: [
20150                     {
20151                         tag: 'input',
20152                         type: 'checkbox'
20153                     },
20154                     {
20155                         tag: 'label'
20156                     }
20157                 ]
20158             }
20159         ]
20160     },
20161     
20162     emptyResult : {
20163         tag: 'div',
20164         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
20165     },
20166     
20167     footer : {
20168         tag: 'div',
20169         cls: 'modal-footer',
20170         cn: [
20171             {
20172                 tag: 'div',
20173                 cls: 'row',
20174                 cn: [
20175                     {
20176                         tag: 'div',
20177                         cls: 'col-xs-6 text-left',
20178                         cn: {
20179                             tag: 'button',
20180                             cls: 'btn btn-danger roo-touch-view-cancel',
20181                             html: 'Cancel'
20182                         }
20183                     },
20184                     {
20185                         tag: 'div',
20186                         cls: 'col-xs-6 text-right',
20187                         cn: {
20188                             tag: 'button',
20189                             cls: 'btn btn-success roo-touch-view-ok',
20190                             html: 'OK'
20191                         }
20192                     }
20193                 ]
20194             }
20195         ]
20196         
20197     }
20198 });
20199
20200 Roo.apply(Roo.bootstrap.form.ComboBox,  {
20201     
20202     touchViewTemplate : {
20203         tag: 'div',
20204         cls: 'modal fade roo-combobox-touch-view',
20205         cn: [
20206             {
20207                 tag: 'div',
20208                 cls: 'modal-dialog',
20209                 style : 'position:fixed', // we have to fix position....
20210                 cn: [
20211                     {
20212                         tag: 'div',
20213                         cls: 'modal-content',
20214                         cn: [
20215                             Roo.bootstrap.form.ComboBox.header,
20216                             Roo.bootstrap.form.ComboBox.body,
20217                             Roo.bootstrap.form.ComboBox.footer
20218                         ]
20219                     }
20220                 ]
20221             }
20222         ]
20223     }
20224 });/*
20225  * Based on:
20226  * Ext JS Library 1.1.1
20227  * Copyright(c) 2006-2007, Ext JS, LLC.
20228  *
20229  * Originally Released Under LGPL - original licence link has changed is not relivant.
20230  *
20231  * Fork - LGPL
20232  * <script type="text/javascript">
20233  */
20234
20235 /**
20236  * @class Roo.View
20237  * @extends Roo.util.Observable
20238  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
20239  * This class also supports single and multi selection modes. <br>
20240  * Create a data model bound view:
20241  <pre><code>
20242  var store = new Roo.data.Store(...);
20243
20244  var view = new Roo.View({
20245     el : "my-element",
20246     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
20247  
20248     singleSelect: true,
20249     selectedClass: "ydataview-selected",
20250     store: store
20251  });
20252
20253  // listen for node click?
20254  view.on("click", function(vw, index, node, e){
20255  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
20256  });
20257
20258  // load XML data
20259  dataModel.load("foobar.xml");
20260  </code></pre>
20261  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
20262  * <br><br>
20263  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
20264  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
20265  * 
20266  * Note: old style constructor is still suported (container, template, config)
20267  * 
20268  * @constructor
20269  * Create a new View
20270  * @param {Object} config The config object
20271  * 
20272  */
20273 Roo.View = function(config, depreciated_tpl, depreciated_config){
20274     
20275     this.parent = false;
20276     
20277     if (typeof(depreciated_tpl) == 'undefined') {
20278         // new way.. - universal constructor.
20279         Roo.apply(this, config);
20280         this.el  = Roo.get(this.el);
20281     } else {
20282         // old format..
20283         this.el  = Roo.get(config);
20284         this.tpl = depreciated_tpl;
20285         Roo.apply(this, depreciated_config);
20286     }
20287     this.wrapEl  = this.el.wrap().wrap();
20288     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20289     
20290     
20291     if(typeof(this.tpl) == "string"){
20292         this.tpl = new Roo.Template(this.tpl);
20293     } else {
20294         // support xtype ctors..
20295         this.tpl = new Roo.factory(this.tpl, Roo);
20296     }
20297     
20298     
20299     this.tpl.compile();
20300     
20301     /** @private */
20302     this.addEvents({
20303         /**
20304          * @event beforeclick
20305          * Fires before a click is processed. Returns false to cancel the default action.
20306          * @param {Roo.View} this
20307          * @param {Number} index The index of the target node
20308          * @param {HTMLElement} node The target node
20309          * @param {Roo.EventObject} e The raw event object
20310          */
20311             "beforeclick" : true,
20312         /**
20313          * @event click
20314          * Fires when a template node is clicked.
20315          * @param {Roo.View} this
20316          * @param {Number} index The index of the target node
20317          * @param {HTMLElement} node The target node
20318          * @param {Roo.EventObject} e The raw event object
20319          */
20320             "click" : true,
20321         /**
20322          * @event dblclick
20323          * Fires when a template node is double clicked.
20324          * @param {Roo.View} this
20325          * @param {Number} index The index of the target node
20326          * @param {HTMLElement} node The target node
20327          * @param {Roo.EventObject} e The raw event object
20328          */
20329             "dblclick" : true,
20330         /**
20331          * @event contextmenu
20332          * Fires when a template node is right clicked.
20333          * @param {Roo.View} this
20334          * @param {Number} index The index of the target node
20335          * @param {HTMLElement} node The target node
20336          * @param {Roo.EventObject} e The raw event object
20337          */
20338             "contextmenu" : true,
20339         /**
20340          * @event selectionchange
20341          * Fires when the selected nodes change.
20342          * @param {Roo.View} this
20343          * @param {Array} selections Array of the selected nodes
20344          */
20345             "selectionchange" : true,
20346     
20347         /**
20348          * @event beforeselect
20349          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20350          * @param {Roo.View} this
20351          * @param {HTMLElement} node The node to be selected
20352          * @param {Array} selections Array of currently selected nodes
20353          */
20354             "beforeselect" : true,
20355         /**
20356          * @event preparedata
20357          * Fires on every row to render, to allow you to change the data.
20358          * @param {Roo.View} this
20359          * @param {Object} data to be rendered (change this)
20360          */
20361           "preparedata" : true
20362           
20363           
20364         });
20365
20366
20367
20368     this.el.on({
20369         "click": this.onClick,
20370         "dblclick": this.onDblClick,
20371         "contextmenu": this.onContextMenu,
20372         scope:this
20373     });
20374
20375     this.selections = [];
20376     this.nodes = [];
20377     this.cmp = new Roo.CompositeElementLite([]);
20378     if(this.store){
20379         this.store = Roo.factory(this.store, Roo.data);
20380         this.setStore(this.store, true);
20381     }
20382     
20383     if ( this.footer && this.footer.xtype) {
20384            
20385          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20386         
20387         this.footer.dataSource = this.store;
20388         this.footer.container = fctr;
20389         this.footer = Roo.factory(this.footer, Roo);
20390         fctr.insertFirst(this.el);
20391         
20392         // this is a bit insane - as the paging toolbar seems to detach the el..
20393 //        dom.parentNode.parentNode.parentNode
20394          // they get detached?
20395     }
20396     
20397     
20398     Roo.View.superclass.constructor.call(this);
20399     
20400     
20401 };
20402
20403 Roo.extend(Roo.View, Roo.util.Observable, {
20404     
20405      /**
20406      * @cfg {Roo.data.Store} store Data store to load data from.
20407      */
20408     store : false,
20409     
20410     /**
20411      * @cfg {String|Roo.Element} el The container element.
20412      */
20413     el : '',
20414     
20415     /**
20416      * @cfg {String|Roo.Template} tpl The template used by this View 
20417      */
20418     tpl : false,
20419     /**
20420      * @cfg {String} dataName the named area of the template to use as the data area
20421      *                          Works with domtemplates roo-name="name"
20422      */
20423     dataName: false,
20424     /**
20425      * @cfg {String} selectedClass The css class to add to selected nodes
20426      */
20427     selectedClass : "x-view-selected",
20428      /**
20429      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20430      */
20431     emptyText : "",
20432     
20433     /**
20434      * @cfg {String} text to display on mask (default Loading)
20435      */
20436     mask : false,
20437     /**
20438      * @cfg {Boolean} multiSelect Allow multiple selection
20439      */
20440     multiSelect : false,
20441     /**
20442      * @cfg {Boolean} singleSelect Allow single selection
20443      */
20444     singleSelect:  false,
20445     
20446     /**
20447      * @cfg {Boolean} toggleSelect - selecting 
20448      */
20449     toggleSelect : false,
20450     
20451     /**
20452      * @cfg {Boolean} tickable - selecting 
20453      */
20454     tickable : false,
20455     
20456     /**
20457      * Returns the element this view is bound to.
20458      * @return {Roo.Element}
20459      */
20460     getEl : function(){
20461         return this.wrapEl;
20462     },
20463     
20464     
20465
20466     /**
20467      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20468      */
20469     refresh : function(){
20470         //Roo.log('refresh');
20471         var t = this.tpl;
20472         
20473         // if we are using something like 'domtemplate', then
20474         // the what gets used is:
20475         // t.applySubtemplate(NAME, data, wrapping data..)
20476         // the outer template then get' applied with
20477         //     the store 'extra data'
20478         // and the body get's added to the
20479         //      roo-name="data" node?
20480         //      <span class='roo-tpl-{name}'></span> ?????
20481         
20482         
20483         
20484         this.clearSelections();
20485         this.el.update("");
20486         var html = [];
20487         var records = this.store.getRange();
20488         if(records.length < 1) {
20489             
20490             // is this valid??  = should it render a template??
20491             
20492             this.el.update(this.emptyText);
20493             return;
20494         }
20495         var el = this.el;
20496         if (this.dataName) {
20497             this.el.update(t.apply(this.store.meta)); //????
20498             el = this.el.child('.roo-tpl-' + this.dataName);
20499         }
20500         
20501         for(var i = 0, len = records.length; i < len; i++){
20502             var data = this.prepareData(records[i].data, i, records[i]);
20503             this.fireEvent("preparedata", this, data, i, records[i]);
20504             
20505             var d = Roo.apply({}, data);
20506             
20507             if(this.tickable){
20508                 Roo.apply(d, {'roo-id' : Roo.id()});
20509                 
20510                 var _this = this;
20511             
20512                 Roo.each(this.parent.item, function(item){
20513                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20514                         return;
20515                     }
20516                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20517                 });
20518             }
20519             
20520             html[html.length] = Roo.util.Format.trim(
20521                 this.dataName ?
20522                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20523                     t.apply(d)
20524             );
20525         }
20526         
20527         
20528         
20529         el.update(html.join(""));
20530         this.nodes = el.dom.childNodes;
20531         this.updateIndexes(0);
20532     },
20533     
20534
20535     /**
20536      * Function to override to reformat the data that is sent to
20537      * the template for each node.
20538      * DEPRICATED - use the preparedata event handler.
20539      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20540      * a JSON object for an UpdateManager bound view).
20541      */
20542     prepareData : function(data, index, record)
20543     {
20544         this.fireEvent("preparedata", this, data, index, record);
20545         return data;
20546     },
20547
20548     onUpdate : function(ds, record){
20549         // Roo.log('on update');   
20550         this.clearSelections();
20551         var index = this.store.indexOf(record);
20552         var n = this.nodes[index];
20553         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20554         n.parentNode.removeChild(n);
20555         this.updateIndexes(index, index);
20556     },
20557
20558     
20559     
20560 // --------- FIXME     
20561     onAdd : function(ds, records, index)
20562     {
20563         //Roo.log(['on Add', ds, records, index] );        
20564         this.clearSelections();
20565         if(this.nodes.length == 0){
20566             this.refresh();
20567             return;
20568         }
20569         var n = this.nodes[index];
20570         for(var i = 0, len = records.length; i < len; i++){
20571             var d = this.prepareData(records[i].data, i, records[i]);
20572             if(n){
20573                 this.tpl.insertBefore(n, d);
20574             }else{
20575                 
20576                 this.tpl.append(this.el, d);
20577             }
20578         }
20579         this.updateIndexes(index);
20580     },
20581
20582     onRemove : function(ds, record, index){
20583        // Roo.log('onRemove');
20584         this.clearSelections();
20585         var el = this.dataName  ?
20586             this.el.child('.roo-tpl-' + this.dataName) :
20587             this.el; 
20588         
20589         el.dom.removeChild(this.nodes[index]);
20590         this.updateIndexes(index);
20591     },
20592
20593     /**
20594      * Refresh an individual node.
20595      * @param {Number} index
20596      */
20597     refreshNode : function(index){
20598         this.onUpdate(this.store, this.store.getAt(index));
20599     },
20600
20601     updateIndexes : function(startIndex, endIndex){
20602         var ns = this.nodes;
20603         startIndex = startIndex || 0;
20604         endIndex = endIndex || ns.length - 1;
20605         for(var i = startIndex; i <= endIndex; i++){
20606             ns[i].nodeIndex = i;
20607         }
20608     },
20609
20610     /**
20611      * Changes the data store this view uses and refresh the view.
20612      * @param {Store} store
20613      */
20614     setStore : function(store, initial){
20615         if(!initial && this.store){
20616             this.store.un("datachanged", this.refresh);
20617             this.store.un("add", this.onAdd);
20618             this.store.un("remove", this.onRemove);
20619             this.store.un("update", this.onUpdate);
20620             this.store.un("clear", this.refresh);
20621             this.store.un("beforeload", this.onBeforeLoad);
20622             this.store.un("load", this.onLoad);
20623             this.store.un("loadexception", this.onLoad);
20624         }
20625         if(store){
20626           
20627             store.on("datachanged", this.refresh, this);
20628             store.on("add", this.onAdd, this);
20629             store.on("remove", this.onRemove, this);
20630             store.on("update", this.onUpdate, this);
20631             store.on("clear", this.refresh, this);
20632             store.on("beforeload", this.onBeforeLoad, this);
20633             store.on("load", this.onLoad, this);
20634             store.on("loadexception", this.onLoad, this);
20635         }
20636         
20637         if(store){
20638             this.refresh();
20639         }
20640     },
20641     /**
20642      * onbeforeLoad - masks the loading area.
20643      *
20644      */
20645     onBeforeLoad : function(store,opts)
20646     {
20647          //Roo.log('onBeforeLoad');   
20648         if (!opts.add) {
20649             this.el.update("");
20650         }
20651         this.el.mask(this.mask ? this.mask : "Loading" ); 
20652     },
20653     onLoad : function ()
20654     {
20655         this.el.unmask();
20656     },
20657     
20658
20659     /**
20660      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20661      * @param {HTMLElement} node
20662      * @return {HTMLElement} The template node
20663      */
20664     findItemFromChild : function(node){
20665         var el = this.dataName  ?
20666             this.el.child('.roo-tpl-' + this.dataName,true) :
20667             this.el.dom; 
20668         
20669         if(!node || node.parentNode == el){
20670                     return node;
20671             }
20672             var p = node.parentNode;
20673             while(p && p != el){
20674             if(p.parentNode == el){
20675                 return p;
20676             }
20677             p = p.parentNode;
20678         }
20679             return null;
20680     },
20681
20682     /** @ignore */
20683     onClick : function(e){
20684         var item = this.findItemFromChild(e.getTarget());
20685         if(item){
20686             var index = this.indexOf(item);
20687             if(this.onItemClick(item, index, e) !== false){
20688                 this.fireEvent("click", this, index, item, e);
20689             }
20690         }else{
20691             this.clearSelections();
20692         }
20693     },
20694
20695     /** @ignore */
20696     onContextMenu : function(e){
20697         var item = this.findItemFromChild(e.getTarget());
20698         if(item){
20699             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20700         }
20701     },
20702
20703     /** @ignore */
20704     onDblClick : function(e){
20705         var item = this.findItemFromChild(e.getTarget());
20706         if(item){
20707             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20708         }
20709     },
20710
20711     onItemClick : function(item, index, e)
20712     {
20713         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20714             return false;
20715         }
20716         if (this.toggleSelect) {
20717             var m = this.isSelected(item) ? 'unselect' : 'select';
20718             //Roo.log(m);
20719             var _t = this;
20720             _t[m](item, true, false);
20721             return true;
20722         }
20723         if(this.multiSelect || this.singleSelect){
20724             if(this.multiSelect && e.shiftKey && this.lastSelection){
20725                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20726             }else{
20727                 this.select(item, this.multiSelect && e.ctrlKey);
20728                 this.lastSelection = item;
20729             }
20730             
20731             if(!this.tickable){
20732                 e.preventDefault();
20733             }
20734             
20735         }
20736         return true;
20737     },
20738
20739     /**
20740      * Get the number of selected nodes.
20741      * @return {Number}
20742      */
20743     getSelectionCount : function(){
20744         return this.selections.length;
20745     },
20746
20747     /**
20748      * Get the currently selected nodes.
20749      * @return {Array} An array of HTMLElements
20750      */
20751     getSelectedNodes : function(){
20752         return this.selections;
20753     },
20754
20755     /**
20756      * Get the indexes of the selected nodes.
20757      * @return {Array}
20758      */
20759     getSelectedIndexes : function(){
20760         var indexes = [], s = this.selections;
20761         for(var i = 0, len = s.length; i < len; i++){
20762             indexes.push(s[i].nodeIndex);
20763         }
20764         return indexes;
20765     },
20766
20767     /**
20768      * Clear all selections
20769      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20770      */
20771     clearSelections : function(suppressEvent){
20772         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20773             this.cmp.elements = this.selections;
20774             this.cmp.removeClass(this.selectedClass);
20775             this.selections = [];
20776             if(!suppressEvent){
20777                 this.fireEvent("selectionchange", this, this.selections);
20778             }
20779         }
20780     },
20781
20782     /**
20783      * Returns true if the passed node is selected
20784      * @param {HTMLElement/Number} node The node or node index
20785      * @return {Boolean}
20786      */
20787     isSelected : function(node){
20788         var s = this.selections;
20789         if(s.length < 1){
20790             return false;
20791         }
20792         node = this.getNode(node);
20793         return s.indexOf(node) !== -1;
20794     },
20795
20796     /**
20797      * Selects nodes.
20798      * @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
20799      * @param {Boolean} keepExisting (optional) true to keep existing selections
20800      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20801      */
20802     select : function(nodeInfo, keepExisting, suppressEvent){
20803         if(nodeInfo instanceof Array){
20804             if(!keepExisting){
20805                 this.clearSelections(true);
20806             }
20807             for(var i = 0, len = nodeInfo.length; i < len; i++){
20808                 this.select(nodeInfo[i], true, true);
20809             }
20810             return;
20811         } 
20812         var node = this.getNode(nodeInfo);
20813         if(!node || this.isSelected(node)){
20814             return; // already selected.
20815         }
20816         if(!keepExisting){
20817             this.clearSelections(true);
20818         }
20819         
20820         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20821             Roo.fly(node).addClass(this.selectedClass);
20822             this.selections.push(node);
20823             if(!suppressEvent){
20824                 this.fireEvent("selectionchange", this, this.selections);
20825             }
20826         }
20827         
20828         
20829     },
20830       /**
20831      * Unselects nodes.
20832      * @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
20833      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20834      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20835      */
20836     unselect : function(nodeInfo, keepExisting, suppressEvent)
20837     {
20838         if(nodeInfo instanceof Array){
20839             Roo.each(this.selections, function(s) {
20840                 this.unselect(s, nodeInfo);
20841             }, this);
20842             return;
20843         }
20844         var node = this.getNode(nodeInfo);
20845         if(!node || !this.isSelected(node)){
20846             //Roo.log("not selected");
20847             return; // not selected.
20848         }
20849         // fireevent???
20850         var ns = [];
20851         Roo.each(this.selections, function(s) {
20852             if (s == node ) {
20853                 Roo.fly(node).removeClass(this.selectedClass);
20854
20855                 return;
20856             }
20857             ns.push(s);
20858         },this);
20859         
20860         this.selections= ns;
20861         this.fireEvent("selectionchange", this, this.selections);
20862     },
20863
20864     /**
20865      * Gets a template node.
20866      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20867      * @return {HTMLElement} The node or null if it wasn't found
20868      */
20869     getNode : function(nodeInfo){
20870         if(typeof nodeInfo == "string"){
20871             return document.getElementById(nodeInfo);
20872         }else if(typeof nodeInfo == "number"){
20873             return this.nodes[nodeInfo];
20874         }
20875         return nodeInfo;
20876     },
20877
20878     /**
20879      * Gets a range template nodes.
20880      * @param {Number} startIndex
20881      * @param {Number} endIndex
20882      * @return {Array} An array of nodes
20883      */
20884     getNodes : function(start, end){
20885         var ns = this.nodes;
20886         start = start || 0;
20887         end = typeof end == "undefined" ? ns.length - 1 : end;
20888         var nodes = [];
20889         if(start <= end){
20890             for(var i = start; i <= end; i++){
20891                 nodes.push(ns[i]);
20892             }
20893         } else{
20894             for(var i = start; i >= end; i--){
20895                 nodes.push(ns[i]);
20896             }
20897         }
20898         return nodes;
20899     },
20900
20901     /**
20902      * Finds the index of the passed node
20903      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20904      * @return {Number} The index of the node or -1
20905      */
20906     indexOf : function(node){
20907         node = this.getNode(node);
20908         if(typeof node.nodeIndex == "number"){
20909             return node.nodeIndex;
20910         }
20911         var ns = this.nodes;
20912         for(var i = 0, len = ns.length; i < len; i++){
20913             if(ns[i] == node){
20914                 return i;
20915             }
20916         }
20917         return -1;
20918     }
20919 });
20920 /*
20921  * - LGPL
20922  *
20923  * based on jquery fullcalendar
20924  * 
20925  */
20926
20927 Roo.bootstrap = Roo.bootstrap || {};
20928 /**
20929  * @class Roo.bootstrap.Calendar
20930  * @extends Roo.bootstrap.Component
20931  * Bootstrap Calendar class
20932  * @cfg {Boolean} loadMask (true|false) default false
20933  * @cfg {Object} header generate the user specific header of the calendar, default false
20934
20935  * @constructor
20936  * Create a new Container
20937  * @param {Object} config The config object
20938  */
20939
20940
20941
20942 Roo.bootstrap.Calendar = function(config){
20943     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20944      this.addEvents({
20945         /**
20946              * @event select
20947              * Fires when a date is selected
20948              * @param {DatePicker} this
20949              * @param {Date} date The selected date
20950              */
20951         'select': true,
20952         /**
20953              * @event monthchange
20954              * Fires when the displayed month changes 
20955              * @param {DatePicker} this
20956              * @param {Date} date The selected month
20957              */
20958         'monthchange': true,
20959         /**
20960              * @event evententer
20961              * Fires when mouse over an event
20962              * @param {Calendar} this
20963              * @param {event} Event
20964              */
20965         'evententer': true,
20966         /**
20967              * @event eventleave
20968              * Fires when the mouse leaves an
20969              * @param {Calendar} this
20970              * @param {event}
20971              */
20972         'eventleave': true,
20973         /**
20974              * @event eventclick
20975              * Fires when the mouse click an
20976              * @param {Calendar} this
20977              * @param {event}
20978              */
20979         'eventclick': true
20980         
20981     });
20982
20983 };
20984
20985 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20986     
20987           /**
20988      * @cfg {Roo.data.Store} store
20989      * The data source for the calendar
20990      */
20991         store : false,
20992      /**
20993      * @cfg {Number} startDay
20994      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20995      */
20996     startDay : 0,
20997     
20998     loadMask : false,
20999     
21000     header : false,
21001       
21002     getAutoCreate : function(){
21003         
21004         
21005         var fc_button = function(name, corner, style, content ) {
21006             return Roo.apply({},{
21007                 tag : 'span',
21008                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
21009                          (corner.length ?
21010                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
21011                             ''
21012                         ),
21013                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
21014                 unselectable: 'on'
21015             });
21016         };
21017         
21018         var header = {};
21019         
21020         if(!this.header){
21021             header = {
21022                 tag : 'table',
21023                 cls : 'fc-header',
21024                 style : 'width:100%',
21025                 cn : [
21026                     {
21027                         tag: 'tr',
21028                         cn : [
21029                             {
21030                                 tag : 'td',
21031                                 cls : 'fc-header-left',
21032                                 cn : [
21033                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
21034                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
21035                                     { tag: 'span', cls: 'fc-header-space' },
21036                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
21037
21038
21039                                 ]
21040                             },
21041
21042                             {
21043                                 tag : 'td',
21044                                 cls : 'fc-header-center',
21045                                 cn : [
21046                                     {
21047                                         tag: 'span',
21048                                         cls: 'fc-header-title',
21049                                         cn : {
21050                                             tag: 'H2',
21051                                             html : 'month / year'
21052                                         }
21053                                     }
21054
21055                                 ]
21056                             },
21057                             {
21058                                 tag : 'td',
21059                                 cls : 'fc-header-right',
21060                                 cn : [
21061                               /*      fc_button('month', 'left', '', 'month' ),
21062                                     fc_button('week', '', '', 'week' ),
21063                                     fc_button('day', 'right', '', 'day' )
21064                                 */    
21065
21066                                 ]
21067                             }
21068
21069                         ]
21070                     }
21071                 ]
21072             };
21073         }
21074         
21075         header = this.header;
21076         
21077        
21078         var cal_heads = function() {
21079             var ret = [];
21080             // fixme - handle this.
21081             
21082             for (var i =0; i < Date.dayNames.length; i++) {
21083                 var d = Date.dayNames[i];
21084                 ret.push({
21085                     tag: 'th',
21086                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
21087                     html : d.substring(0,3)
21088                 });
21089                 
21090             }
21091             ret[0].cls += ' fc-first';
21092             ret[6].cls += ' fc-last';
21093             return ret;
21094         };
21095         var cal_cell = function(n) {
21096             return  {
21097                 tag: 'td',
21098                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
21099                 cn : [
21100                     {
21101                         cn : [
21102                             {
21103                                 cls: 'fc-day-number',
21104                                 html: 'D'
21105                             },
21106                             {
21107                                 cls: 'fc-day-content',
21108                              
21109                                 cn : [
21110                                      {
21111                                         style: 'position: relative;' // height: 17px;
21112                                     }
21113                                 ]
21114                             }
21115                             
21116                             
21117                         ]
21118                     }
21119                 ]
21120                 
21121             }
21122         };
21123         var cal_rows = function() {
21124             
21125             var ret = [];
21126             for (var r = 0; r < 6; r++) {
21127                 var row= {
21128                     tag : 'tr',
21129                     cls : 'fc-week',
21130                     cn : []
21131                 };
21132                 
21133                 for (var i =0; i < Date.dayNames.length; i++) {
21134                     var d = Date.dayNames[i];
21135                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
21136
21137                 }
21138                 row.cn[0].cls+=' fc-first';
21139                 row.cn[0].cn[0].style = 'min-height:90px';
21140                 row.cn[6].cls+=' fc-last';
21141                 ret.push(row);
21142                 
21143             }
21144             ret[0].cls += ' fc-first';
21145             ret[4].cls += ' fc-prev-last';
21146             ret[5].cls += ' fc-last';
21147             return ret;
21148             
21149         };
21150         
21151         var cal_table = {
21152             tag: 'table',
21153             cls: 'fc-border-separate',
21154             style : 'width:100%',
21155             cellspacing  : 0,
21156             cn : [
21157                 { 
21158                     tag: 'thead',
21159                     cn : [
21160                         { 
21161                             tag: 'tr',
21162                             cls : 'fc-first fc-last',
21163                             cn : cal_heads()
21164                         }
21165                     ]
21166                 },
21167                 { 
21168                     tag: 'tbody',
21169                     cn : cal_rows()
21170                 }
21171                   
21172             ]
21173         };
21174          
21175          var cfg = {
21176             cls : 'fc fc-ltr',
21177             cn : [
21178                 header,
21179                 {
21180                     cls : 'fc-content',
21181                     style : "position: relative;",
21182                     cn : [
21183                         {
21184                             cls : 'fc-view fc-view-month fc-grid',
21185                             style : 'position: relative',
21186                             unselectable : 'on',
21187                             cn : [
21188                                 {
21189                                     cls : 'fc-event-container',
21190                                     style : 'position:absolute;z-index:8;top:0;left:0;'
21191                                 },
21192                                 cal_table
21193                             ]
21194                         }
21195                     ]
21196     
21197                 }
21198            ] 
21199             
21200         };
21201         
21202          
21203         
21204         return cfg;
21205     },
21206     
21207     
21208     initEvents : function()
21209     {
21210         if(!this.store){
21211             throw "can not find store for calendar";
21212         }
21213         
21214         var mark = {
21215             tag: "div",
21216             cls:"x-dlg-mask",
21217             style: "text-align:center",
21218             cn: [
21219                 {
21220                     tag: "div",
21221                     style: "background-color:white;width:50%;margin:250 auto",
21222                     cn: [
21223                         {
21224                             tag: "img",
21225                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
21226                         },
21227                         {
21228                             tag: "span",
21229                             html: "Loading"
21230                         }
21231                         
21232                     ]
21233                 }
21234             ]
21235         };
21236         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
21237         
21238         var size = this.el.select('.fc-content', true).first().getSize();
21239         this.maskEl.setSize(size.width, size.height);
21240         this.maskEl.enableDisplayMode("block");
21241         if(!this.loadMask){
21242             this.maskEl.hide();
21243         }
21244         
21245         this.store = Roo.factory(this.store, Roo.data);
21246         this.store.on('load', this.onLoad, this);
21247         this.store.on('beforeload', this.onBeforeLoad, this);
21248         
21249         this.resize();
21250         
21251         this.cells = this.el.select('.fc-day',true);
21252         //Roo.log(this.cells);
21253         this.textNodes = this.el.query('.fc-day-number');
21254         this.cells.addClassOnOver('fc-state-hover');
21255         
21256         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
21257         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
21258         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
21259         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
21260         
21261         this.on('monthchange', this.onMonthChange, this);
21262         
21263         this.update(new Date().clearTime());
21264     },
21265     
21266     resize : function() {
21267         var sz  = this.el.getSize();
21268         
21269         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21270         this.el.select('.fc-day-content div',true).setHeight(34);
21271     },
21272     
21273     
21274     // private
21275     showPrevMonth : function(e){
21276         this.update(this.activeDate.add("mo", -1));
21277     },
21278     showToday : function(e){
21279         this.update(new Date().clearTime());
21280     },
21281     // private
21282     showNextMonth : function(e){
21283         this.update(this.activeDate.add("mo", 1));
21284     },
21285
21286     // private
21287     showPrevYear : function(){
21288         this.update(this.activeDate.add("y", -1));
21289     },
21290
21291     // private
21292     showNextYear : function(){
21293         this.update(this.activeDate.add("y", 1));
21294     },
21295
21296     
21297    // private
21298     update : function(date)
21299     {
21300         var vd = this.activeDate;
21301         this.activeDate = date;
21302 //        if(vd && this.el){
21303 //            var t = date.getTime();
21304 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21305 //                Roo.log('using add remove');
21306 //                
21307 //                this.fireEvent('monthchange', this, date);
21308 //                
21309 //                this.cells.removeClass("fc-state-highlight");
21310 //                this.cells.each(function(c){
21311 //                   if(c.dateValue == t){
21312 //                       c.addClass("fc-state-highlight");
21313 //                       setTimeout(function(){
21314 //                            try{c.dom.firstChild.focus();}catch(e){}
21315 //                       }, 50);
21316 //                       return false;
21317 //                   }
21318 //                   return true;
21319 //                });
21320 //                return;
21321 //            }
21322 //        }
21323         
21324         var days = date.getDaysInMonth();
21325         
21326         var firstOfMonth = date.getFirstDateOfMonth();
21327         var startingPos = firstOfMonth.getDay()-this.startDay;
21328         
21329         if(startingPos < this.startDay){
21330             startingPos += 7;
21331         }
21332         
21333         var pm = date.add(Date.MONTH, -1);
21334         var prevStart = pm.getDaysInMonth()-startingPos;
21335 //        
21336         this.cells = this.el.select('.fc-day',true);
21337         this.textNodes = this.el.query('.fc-day-number');
21338         this.cells.addClassOnOver('fc-state-hover');
21339         
21340         var cells = this.cells.elements;
21341         var textEls = this.textNodes;
21342         
21343         Roo.each(cells, function(cell){
21344             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21345         });
21346         
21347         days += startingPos;
21348
21349         // convert everything to numbers so it's fast
21350         var day = 86400000;
21351         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21352         //Roo.log(d);
21353         //Roo.log(pm);
21354         //Roo.log(prevStart);
21355         
21356         var today = new Date().clearTime().getTime();
21357         var sel = date.clearTime().getTime();
21358         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21359         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21360         var ddMatch = this.disabledDatesRE;
21361         var ddText = this.disabledDatesText;
21362         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21363         var ddaysText = this.disabledDaysText;
21364         var format = this.format;
21365         
21366         var setCellClass = function(cal, cell){
21367             cell.row = 0;
21368             cell.events = [];
21369             cell.more = [];
21370             //Roo.log('set Cell Class');
21371             cell.title = "";
21372             var t = d.getTime();
21373             
21374             //Roo.log(d);
21375             
21376             cell.dateValue = t;
21377             if(t == today){
21378                 cell.className += " fc-today";
21379                 cell.className += " fc-state-highlight";
21380                 cell.title = cal.todayText;
21381             }
21382             if(t == sel){
21383                 // disable highlight in other month..
21384                 //cell.className += " fc-state-highlight";
21385                 
21386             }
21387             // disabling
21388             if(t < min) {
21389                 cell.className = " fc-state-disabled";
21390                 cell.title = cal.minText;
21391                 return;
21392             }
21393             if(t > max) {
21394                 cell.className = " fc-state-disabled";
21395                 cell.title = cal.maxText;
21396                 return;
21397             }
21398             if(ddays){
21399                 if(ddays.indexOf(d.getDay()) != -1){
21400                     cell.title = ddaysText;
21401                     cell.className = " fc-state-disabled";
21402                 }
21403             }
21404             if(ddMatch && format){
21405                 var fvalue = d.dateFormat(format);
21406                 if(ddMatch.test(fvalue)){
21407                     cell.title = ddText.replace("%0", fvalue);
21408                     cell.className = " fc-state-disabled";
21409                 }
21410             }
21411             
21412             if (!cell.initialClassName) {
21413                 cell.initialClassName = cell.dom.className;
21414             }
21415             
21416             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21417         };
21418
21419         var i = 0;
21420         
21421         for(; i < startingPos; i++) {
21422             textEls[i].innerHTML = (++prevStart);
21423             d.setDate(d.getDate()+1);
21424             
21425             cells[i].className = "fc-past fc-other-month";
21426             setCellClass(this, cells[i]);
21427         }
21428         
21429         var intDay = 0;
21430         
21431         for(; i < days; i++){
21432             intDay = i - startingPos + 1;
21433             textEls[i].innerHTML = (intDay);
21434             d.setDate(d.getDate()+1);
21435             
21436             cells[i].className = ''; // "x-date-active";
21437             setCellClass(this, cells[i]);
21438         }
21439         var extraDays = 0;
21440         
21441         for(; i < 42; i++) {
21442             textEls[i].innerHTML = (++extraDays);
21443             d.setDate(d.getDate()+1);
21444             
21445             cells[i].className = "fc-future fc-other-month";
21446             setCellClass(this, cells[i]);
21447         }
21448         
21449         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21450         
21451         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21452         
21453         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21454         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21455         
21456         if(totalRows != 6){
21457             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21458             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21459         }
21460         
21461         this.fireEvent('monthchange', this, date);
21462         
21463         
21464         /*
21465         if(!this.internalRender){
21466             var main = this.el.dom.firstChild;
21467             var w = main.offsetWidth;
21468             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21469             Roo.fly(main).setWidth(w);
21470             this.internalRender = true;
21471             // opera does not respect the auto grow header center column
21472             // then, after it gets a width opera refuses to recalculate
21473             // without a second pass
21474             if(Roo.isOpera && !this.secondPass){
21475                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21476                 this.secondPass = true;
21477                 this.update.defer(10, this, [date]);
21478             }
21479         }
21480         */
21481         
21482     },
21483     
21484     findCell : function(dt) {
21485         dt = dt.clearTime().getTime();
21486         var ret = false;
21487         this.cells.each(function(c){
21488             //Roo.log("check " +c.dateValue + '?=' + dt);
21489             if(c.dateValue == dt){
21490                 ret = c;
21491                 return false;
21492             }
21493             return true;
21494         });
21495         
21496         return ret;
21497     },
21498     
21499     findCells : function(ev) {
21500         var s = ev.start.clone().clearTime().getTime();
21501        // Roo.log(s);
21502         var e= ev.end.clone().clearTime().getTime();
21503        // Roo.log(e);
21504         var ret = [];
21505         this.cells.each(function(c){
21506              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21507             
21508             if(c.dateValue > e){
21509                 return ;
21510             }
21511             if(c.dateValue < s){
21512                 return ;
21513             }
21514             ret.push(c);
21515         });
21516         
21517         return ret;    
21518     },
21519     
21520 //    findBestRow: function(cells)
21521 //    {
21522 //        var ret = 0;
21523 //        
21524 //        for (var i =0 ; i < cells.length;i++) {
21525 //            ret  = Math.max(cells[i].rows || 0,ret);
21526 //        }
21527 //        return ret;
21528 //        
21529 //    },
21530     
21531     
21532     addItem : function(ev)
21533     {
21534         // look for vertical location slot in
21535         var cells = this.findCells(ev);
21536         
21537 //        ev.row = this.findBestRow(cells);
21538         
21539         // work out the location.
21540         
21541         var crow = false;
21542         var rows = [];
21543         for(var i =0; i < cells.length; i++) {
21544             
21545             cells[i].row = cells[0].row;
21546             
21547             if(i == 0){
21548                 cells[i].row = cells[i].row + 1;
21549             }
21550             
21551             if (!crow) {
21552                 crow = {
21553                     start : cells[i],
21554                     end :  cells[i]
21555                 };
21556                 continue;
21557             }
21558             if (crow.start.getY() == cells[i].getY()) {
21559                 // on same row.
21560                 crow.end = cells[i];
21561                 continue;
21562             }
21563             // different row.
21564             rows.push(crow);
21565             crow = {
21566                 start: cells[i],
21567                 end : cells[i]
21568             };
21569             
21570         }
21571         
21572         rows.push(crow);
21573         ev.els = [];
21574         ev.rows = rows;
21575         ev.cells = cells;
21576         
21577         cells[0].events.push(ev);
21578         
21579         this.calevents.push(ev);
21580     },
21581     
21582     clearEvents: function() {
21583         
21584         if(!this.calevents){
21585             return;
21586         }
21587         
21588         Roo.each(this.cells.elements, function(c){
21589             c.row = 0;
21590             c.events = [];
21591             c.more = [];
21592         });
21593         
21594         Roo.each(this.calevents, function(e) {
21595             Roo.each(e.els, function(el) {
21596                 el.un('mouseenter' ,this.onEventEnter, this);
21597                 el.un('mouseleave' ,this.onEventLeave, this);
21598                 el.remove();
21599             },this);
21600         },this);
21601         
21602         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21603             e.remove();
21604         });
21605         
21606     },
21607     
21608     renderEvents: function()
21609     {   
21610         var _this = this;
21611         
21612         this.cells.each(function(c) {
21613             
21614             if(c.row < 5){
21615                 return;
21616             }
21617             
21618             var ev = c.events;
21619             
21620             var r = 4;
21621             if(c.row != c.events.length){
21622                 r = 4 - (4 - (c.row - c.events.length));
21623             }
21624             
21625             c.events = ev.slice(0, r);
21626             c.more = ev.slice(r);
21627             
21628             if(c.more.length && c.more.length == 1){
21629                 c.events.push(c.more.pop());
21630             }
21631             
21632             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21633             
21634         });
21635             
21636         this.cells.each(function(c) {
21637             
21638             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21639             
21640             
21641             for (var e = 0; e < c.events.length; e++){
21642                 var ev = c.events[e];
21643                 var rows = ev.rows;
21644                 
21645                 for(var i = 0; i < rows.length; i++) {
21646                 
21647                     // how many rows should it span..
21648
21649                     var  cfg = {
21650                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21651                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21652
21653                         unselectable : "on",
21654                         cn : [
21655                             {
21656                                 cls: 'fc-event-inner',
21657                                 cn : [
21658     //                                {
21659     //                                  tag:'span',
21660     //                                  cls: 'fc-event-time',
21661     //                                  html : cells.length > 1 ? '' : ev.time
21662     //                                },
21663                                     {
21664                                       tag:'span',
21665                                       cls: 'fc-event-title',
21666                                       html : String.format('{0}', ev.title)
21667                                     }
21668
21669
21670                                 ]
21671                             },
21672                             {
21673                                 cls: 'ui-resizable-handle ui-resizable-e',
21674                                 html : '&nbsp;&nbsp;&nbsp'
21675                             }
21676
21677                         ]
21678                     };
21679
21680                     if (i == 0) {
21681                         cfg.cls += ' fc-event-start';
21682                     }
21683                     if ((i+1) == rows.length) {
21684                         cfg.cls += ' fc-event-end';
21685                     }
21686
21687                     var ctr = _this.el.select('.fc-event-container',true).first();
21688                     var cg = ctr.createChild(cfg);
21689
21690                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21691                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21692
21693                     var r = (c.more.length) ? 1 : 0;
21694                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21695                     cg.setWidth(ebox.right - sbox.x -2);
21696
21697                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21698                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21699                     cg.on('click', _this.onEventClick, _this, ev);
21700
21701                     ev.els.push(cg);
21702                     
21703                 }
21704                 
21705             }
21706             
21707             
21708             if(c.more.length){
21709                 var  cfg = {
21710                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21711                     style : 'position: absolute',
21712                     unselectable : "on",
21713                     cn : [
21714                         {
21715                             cls: 'fc-event-inner',
21716                             cn : [
21717                                 {
21718                                   tag:'span',
21719                                   cls: 'fc-event-title',
21720                                   html : 'More'
21721                                 }
21722
21723
21724                             ]
21725                         },
21726                         {
21727                             cls: 'ui-resizable-handle ui-resizable-e',
21728                             html : '&nbsp;&nbsp;&nbsp'
21729                         }
21730
21731                     ]
21732                 };
21733
21734                 var ctr = _this.el.select('.fc-event-container',true).first();
21735                 var cg = ctr.createChild(cfg);
21736
21737                 var sbox = c.select('.fc-day-content',true).first().getBox();
21738                 var ebox = c.select('.fc-day-content',true).first().getBox();
21739                 //Roo.log(cg);
21740                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21741                 cg.setWidth(ebox.right - sbox.x -2);
21742
21743                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21744                 
21745             }
21746             
21747         });
21748         
21749         
21750         
21751     },
21752     
21753     onEventEnter: function (e, el,event,d) {
21754         this.fireEvent('evententer', this, el, event);
21755     },
21756     
21757     onEventLeave: function (e, el,event,d) {
21758         this.fireEvent('eventleave', this, el, event);
21759     },
21760     
21761     onEventClick: function (e, el,event,d) {
21762         this.fireEvent('eventclick', this, el, event);
21763     },
21764     
21765     onMonthChange: function () {
21766         this.store.load();
21767     },
21768     
21769     onMoreEventClick: function(e, el, more)
21770     {
21771         var _this = this;
21772         
21773         this.calpopover.placement = 'right';
21774         this.calpopover.setTitle('More');
21775         
21776         this.calpopover.setContent('');
21777         
21778         var ctr = this.calpopover.el.select('.popover-content', true).first();
21779         
21780         Roo.each(more, function(m){
21781             var cfg = {
21782                 cls : 'fc-event-hori fc-event-draggable',
21783                 html : m.title
21784             };
21785             var cg = ctr.createChild(cfg);
21786             
21787             cg.on('click', _this.onEventClick, _this, m);
21788         });
21789         
21790         this.calpopover.show(el);
21791         
21792         
21793     },
21794     
21795     onLoad: function () 
21796     {   
21797         this.calevents = [];
21798         var cal = this;
21799         
21800         if(this.store.getCount() > 0){
21801             this.store.data.each(function(d){
21802                cal.addItem({
21803                     id : d.data.id,
21804                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21805                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21806                     time : d.data.start_time,
21807                     title : d.data.title,
21808                     description : d.data.description,
21809                     venue : d.data.venue
21810                 });
21811             });
21812         }
21813         
21814         this.renderEvents();
21815         
21816         if(this.calevents.length && this.loadMask){
21817             this.maskEl.hide();
21818         }
21819     },
21820     
21821     onBeforeLoad: function()
21822     {
21823         this.clearEvents();
21824         if(this.loadMask){
21825             this.maskEl.show();
21826         }
21827     }
21828 });
21829
21830  
21831  /*
21832  * - LGPL
21833  *
21834  * element
21835  * 
21836  */
21837
21838 /**
21839  * @class Roo.bootstrap.Popover
21840  * @extends Roo.bootstrap.Component
21841  * @parent none builder
21842  * @children Roo.bootstrap.Component
21843  * Bootstrap Popover class
21844  * @cfg {String} html contents of the popover   (or false to use children..)
21845  * @cfg {String} title of popover (or false to hide)
21846  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21847  * @cfg {String} trigger click || hover (or false to trigger manually)
21848  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21849  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21850  *      - if false and it has a 'parent' then it will be automatically added to that element
21851  *      - if string - Roo.get  will be called 
21852  * @cfg {Number} delay - delay before showing
21853  
21854  * @constructor
21855  * Create a new Popover
21856  * @param {Object} config The config object
21857  */
21858
21859 Roo.bootstrap.Popover = function(config){
21860     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21861     
21862     this.addEvents({
21863         // raw events
21864          /**
21865          * @event show
21866          * After the popover show
21867          * 
21868          * @param {Roo.bootstrap.Popover} this
21869          */
21870         "show" : true,
21871         /**
21872          * @event hide
21873          * After the popover hide
21874          * 
21875          * @param {Roo.bootstrap.Popover} this
21876          */
21877         "hide" : true
21878     });
21879 };
21880
21881 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21882     
21883     title: false,
21884     html: false,
21885     
21886     placement : 'right',
21887     trigger : 'hover', // hover
21888     modal : false,
21889     delay : 0,
21890     
21891     over: false,
21892     
21893     can_build_overlaid : false,
21894     
21895     maskEl : false, // the mask element
21896     headerEl : false,
21897     contentEl : false,
21898     alignEl : false, // when show is called with an element - this get's stored.
21899     
21900     getChildContainer : function()
21901     {
21902         return this.contentEl;
21903         
21904     },
21905     getPopoverHeader : function()
21906     {
21907         this.title = true; // flag not to hide it..
21908         this.headerEl.addClass('p-0');
21909         return this.headerEl
21910     },
21911     
21912     
21913     getAutoCreate : function(){
21914          
21915         var cfg = {
21916            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21917            style: 'display:block',
21918            cn : [
21919                 {
21920                     cls : 'arrow'
21921                 },
21922                 {
21923                     cls : 'popover-inner ',
21924                     cn : [
21925                         {
21926                             tag: 'h3',
21927                             cls: 'popover-title popover-header',
21928                             html : this.title === false ? '' : this.title
21929                         },
21930                         {
21931                             cls : 'popover-content popover-body '  + (this.cls || ''),
21932                             html : this.html || ''
21933                         }
21934                     ]
21935                     
21936                 }
21937            ]
21938         };
21939         
21940         return cfg;
21941     },
21942     /**
21943      * @param {string} the title
21944      */
21945     setTitle: function(str)
21946     {
21947         this.title = str;
21948         if (this.el) {
21949             this.headerEl.dom.innerHTML = str;
21950         }
21951         
21952     },
21953     /**
21954      * @param {string} the body content
21955      */
21956     setContent: function(str)
21957     {
21958         this.html = str;
21959         if (this.contentEl) {
21960             this.contentEl.dom.innerHTML = str;
21961         }
21962         
21963     },
21964     // as it get's added to the bottom of the page.
21965     onRender : function(ct, position)
21966     {
21967         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21968         
21969         
21970         
21971         if(!this.el){
21972             var cfg = Roo.apply({},  this.getAutoCreate());
21973             cfg.id = Roo.id();
21974             
21975             if (this.cls) {
21976                 cfg.cls += ' ' + this.cls;
21977             }
21978             if (this.style) {
21979                 cfg.style = this.style;
21980             }
21981             //Roo.log("adding to ");
21982             this.el = Roo.get(document.body).createChild(cfg, position);
21983 //            Roo.log(this.el);
21984         }
21985         
21986         this.contentEl = this.el.select('.popover-content',true).first();
21987         this.headerEl =  this.el.select('.popover-title',true).first();
21988         
21989         var nitems = [];
21990         if(typeof(this.items) != 'undefined'){
21991             var items = this.items;
21992             delete this.items;
21993
21994             for(var i =0;i < items.length;i++) {
21995                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21996             }
21997         }
21998
21999         this.items = nitems;
22000         
22001         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
22002         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
22003         
22004         
22005         
22006         this.initEvents();
22007     },
22008     
22009     resizeMask : function()
22010     {
22011         this.maskEl.setSize(
22012             Roo.lib.Dom.getViewWidth(true),
22013             Roo.lib.Dom.getViewHeight(true)
22014         );
22015     },
22016     
22017     initEvents : function()
22018     {
22019         
22020         if (!this.modal) { 
22021             Roo.bootstrap.Popover.register(this);
22022         }
22023          
22024         this.arrowEl = this.el.select('.arrow',true).first();
22025         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
22026         this.el.enableDisplayMode('block');
22027         this.el.hide();
22028  
22029         
22030         if (this.over === false && !this.parent()) {
22031             return; 
22032         }
22033         if (this.triggers === false) {
22034             return;
22035         }
22036          
22037         // support parent
22038         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
22039         var triggers = this.trigger ? this.trigger.split(' ') : [];
22040         Roo.each(triggers, function(trigger) {
22041         
22042             if (trigger == 'click') {
22043                 on_el.on('click', this.toggle, this);
22044             } else if (trigger != 'manual') {
22045                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
22046                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
22047       
22048                 on_el.on(eventIn  ,this.enter, this);
22049                 on_el.on(eventOut, this.leave, this);
22050             }
22051         }, this);
22052     },
22053     
22054     
22055     // private
22056     timeout : null,
22057     hoverState : null,
22058     
22059     toggle : function () {
22060         this.hoverState == 'in' ? this.leave() : this.enter();
22061     },
22062     
22063     enter : function () {
22064         
22065         clearTimeout(this.timeout);
22066     
22067         this.hoverState = 'in';
22068     
22069         if (!this.delay || !this.delay.show) {
22070             this.show();
22071             return;
22072         }
22073         var _t = this;
22074         this.timeout = setTimeout(function () {
22075             if (_t.hoverState == 'in') {
22076                 _t.show();
22077             }
22078         }, this.delay.show)
22079     },
22080     
22081     leave : function() {
22082         clearTimeout(this.timeout);
22083     
22084         this.hoverState = 'out';
22085     
22086         if (!this.delay || !this.delay.hide) {
22087             this.hide();
22088             return;
22089         }
22090         var _t = this;
22091         this.timeout = setTimeout(function () {
22092             if (_t.hoverState == 'out') {
22093                 _t.hide();
22094             }
22095         }, this.delay.hide)
22096     },
22097     
22098     /**
22099      * update the position of the dialog
22100      * normally this is needed if the popover get's bigger - due to a Table reload etc..
22101      * 
22102      *
22103      */
22104     
22105     doAlign : function()
22106     {
22107         
22108         if (this.alignEl) {
22109             this.updatePosition(this.placement, true);
22110              
22111         } else {
22112             // this is usually just done by the builder = to show the popoup in the middle of the scren.
22113             var es = this.el.getSize();
22114             var x = Roo.lib.Dom.getViewWidth()/2;
22115             var y = Roo.lib.Dom.getViewHeight()/2;
22116             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
22117             
22118         }
22119
22120          
22121          
22122         
22123         
22124     },
22125     
22126     /**
22127      * Show the popover
22128      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
22129      * @param {string} (left|right|top|bottom) position
22130      */
22131     show : function (on_el, placement)
22132     {
22133         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
22134         on_el = on_el || false; // default to false
22135          
22136         if (!on_el) {
22137             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
22138                 on_el = this.parent().el;
22139             } else if (this.over) {
22140                 on_el = Roo.get(this.over);
22141             }
22142             
22143         }
22144         
22145         this.alignEl = Roo.get( on_el );
22146
22147         if (!this.el) {
22148             this.render(document.body);
22149         }
22150         
22151         
22152          
22153         
22154         if (this.title === false) {
22155             this.headerEl.hide();
22156         }
22157         
22158        
22159         this.el.show();
22160         this.el.dom.style.display = 'block';
22161          
22162         this.doAlign();
22163         
22164         //var arrow = this.el.select('.arrow',true).first();
22165         //arrow.set(align[2], 
22166         
22167         this.el.addClass('in');
22168         
22169          
22170         
22171         this.hoverState = 'in';
22172         
22173         if (this.modal) {
22174             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
22175             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
22176             this.maskEl.dom.style.display = 'block';
22177             this.maskEl.addClass('show');
22178         }
22179         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
22180  
22181         this.fireEvent('show', this);
22182         
22183     },
22184     /**
22185      * fire this manually after loading a grid in the table for example
22186      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
22187      * @param {Boolean} try and move it if we cant get right position.
22188      */
22189     updatePosition : function(placement, try_move)
22190     {
22191         // allow for calling with no parameters
22192         placement = placement   ? placement :  this.placement;
22193         try_move = typeof(try_move) == 'undefined' ? true : try_move;
22194         
22195         this.el.removeClass([
22196             'fade','top','bottom', 'left', 'right','in',
22197             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
22198         ]);
22199         this.el.addClass(placement + ' bs-popover-' + placement);
22200         
22201         if (!this.alignEl ) {
22202             return false;
22203         }
22204         
22205         switch (placement) {
22206             case 'right':
22207                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
22208                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
22209                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
22210                     //normal display... or moved up/down.
22211                     this.el.setXY(offset);
22212                     var xy = this.alignEl.getAnchorXY('tr', false);
22213                     xy[0]+=2;xy[1]+=5;
22214                     this.arrowEl.setXY(xy);
22215                     return true;
22216                 }
22217                 // continue through...
22218                 return this.updatePosition('left', false);
22219                 
22220             
22221             case 'left':
22222                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
22223                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
22224                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
22225                     //normal display... or moved up/down.
22226                     this.el.setXY(offset);
22227                     var xy = this.alignEl.getAnchorXY('tl', false);
22228                     xy[0]-=10;xy[1]+=5; // << fix me
22229                     this.arrowEl.setXY(xy);
22230                     return true;
22231                 }
22232                 // call self...
22233                 return this.updatePosition('right', false);
22234             
22235             case 'top':
22236                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
22237                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
22238                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
22239                     //normal display... or moved up/down.
22240                     this.el.setXY(offset);
22241                     var xy = this.alignEl.getAnchorXY('t', false);
22242                     xy[1]-=10; // << fix me
22243                     this.arrowEl.setXY(xy);
22244                     return true;
22245                 }
22246                 // fall through
22247                return this.updatePosition('bottom', false);
22248             
22249             case 'bottom':
22250                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
22251                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
22252                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
22253                     //normal display... or moved up/down.
22254                     this.el.setXY(offset);
22255                     var xy = this.alignEl.getAnchorXY('b', false);
22256                      xy[1]+=2; // << fix me
22257                     this.arrowEl.setXY(xy);
22258                     return true;
22259                 }
22260                 // fall through
22261                 return this.updatePosition('top', false);
22262                 
22263             
22264         }
22265         
22266         
22267         return false;
22268     },
22269     
22270     hide : function()
22271     {
22272         this.el.setXY([0,0]);
22273         this.el.removeClass('in');
22274         this.el.hide();
22275         this.hoverState = null;
22276         this.maskEl.hide(); // always..
22277         this.fireEvent('hide', this);
22278     }
22279     
22280 });
22281
22282
22283 Roo.apply(Roo.bootstrap.Popover, {
22284
22285     alignment : {
22286         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22287         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22288         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22289         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22290     },
22291     
22292     zIndex : 20001,
22293
22294     clickHander : false,
22295     
22296     
22297
22298     onMouseDown : function(e)
22299     {
22300         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22301             /// what is nothing is showing..
22302             this.hideAll();
22303         }
22304          
22305     },
22306     
22307     
22308     popups : [],
22309     
22310     register : function(popup)
22311     {
22312         if (!Roo.bootstrap.Popover.clickHandler) {
22313             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22314         }
22315         // hide other popups.
22316         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22317         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22318         this.hideAll(); //<< why?
22319         //this.popups.push(popup);
22320     },
22321     hideAll : function()
22322     {
22323         this.popups.forEach(function(p) {
22324             p.hide();
22325         });
22326     },
22327     onShow : function() {
22328         Roo.bootstrap.Popover.popups.push(this);
22329     },
22330     onHide : function() {
22331         Roo.bootstrap.Popover.popups.remove(this);
22332     } 
22333
22334 });
22335 /**
22336  * @class Roo.bootstrap.PopoverNav
22337  * @extends Roo.bootstrap.nav.Simplebar
22338  * @parent Roo.bootstrap.Popover
22339  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22340  * @licence LGPL
22341  * Bootstrap Popover header navigation class
22342  * FIXME? should this go under nav?
22343  *
22344  * 
22345  * @constructor
22346  * Create a new Popover Header Navigation 
22347  * @param {Object} config The config object
22348  */
22349
22350 Roo.bootstrap.PopoverNav = function(config){
22351     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22352 };
22353
22354 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22355     
22356     
22357     container_method : 'getPopoverHeader' 
22358     
22359      
22360     
22361     
22362    
22363 });
22364
22365  
22366
22367  /*
22368  * - LGPL
22369  *
22370  * Progress
22371  * 
22372  */
22373
22374 /**
22375  * @class Roo.bootstrap.Progress
22376  * @extends Roo.bootstrap.Component
22377  * @children Roo.bootstrap.ProgressBar
22378  * Bootstrap Progress class
22379  * @cfg {Boolean} striped striped of the progress bar
22380  * @cfg {Boolean} active animated of the progress bar
22381  * 
22382  * 
22383  * @constructor
22384  * Create a new Progress
22385  * @param {Object} config The config object
22386  */
22387
22388 Roo.bootstrap.Progress = function(config){
22389     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22390 };
22391
22392 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22393     
22394     striped : false,
22395     active: false,
22396     
22397     getAutoCreate : function(){
22398         var cfg = {
22399             tag: 'div',
22400             cls: 'progress'
22401         };
22402         
22403         
22404         if(this.striped){
22405             cfg.cls += ' progress-striped';
22406         }
22407       
22408         if(this.active){
22409             cfg.cls += ' active';
22410         }
22411         
22412         
22413         return cfg;
22414     }
22415    
22416 });
22417
22418  
22419
22420  /*
22421  * - LGPL
22422  *
22423  * ProgressBar
22424  * 
22425  */
22426
22427 /**
22428  * @class Roo.bootstrap.ProgressBar
22429  * @extends Roo.bootstrap.Component
22430  * Bootstrap ProgressBar class
22431  * @cfg {Number} aria_valuenow aria-value now
22432  * @cfg {Number} aria_valuemin aria-value min
22433  * @cfg {Number} aria_valuemax aria-value max
22434  * @cfg {String} label label for the progress bar
22435  * @cfg {String} panel (success | info | warning | danger )
22436  * @cfg {String} role role of the progress bar
22437  * @cfg {String} sr_only text
22438  * 
22439  * 
22440  * @constructor
22441  * Create a new ProgressBar
22442  * @param {Object} config The config object
22443  */
22444
22445 Roo.bootstrap.ProgressBar = function(config){
22446     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22447 };
22448
22449 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22450     
22451     aria_valuenow : 0,
22452     aria_valuemin : 0,
22453     aria_valuemax : 100,
22454     label : false,
22455     panel : false,
22456     role : false,
22457     sr_only: false,
22458     
22459     getAutoCreate : function()
22460     {
22461         
22462         var cfg = {
22463             tag: 'div',
22464             cls: 'progress-bar',
22465             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22466         };
22467         
22468         if(this.sr_only){
22469             cfg.cn = {
22470                 tag: 'span',
22471                 cls: 'sr-only',
22472                 html: this.sr_only
22473             }
22474         }
22475         
22476         if(this.role){
22477             cfg.role = this.role;
22478         }
22479         
22480         if(this.aria_valuenow){
22481             cfg['aria-valuenow'] = this.aria_valuenow;
22482         }
22483         
22484         if(this.aria_valuemin){
22485             cfg['aria-valuemin'] = this.aria_valuemin;
22486         }
22487         
22488         if(this.aria_valuemax){
22489             cfg['aria-valuemax'] = this.aria_valuemax;
22490         }
22491         
22492         if(this.label && !this.sr_only){
22493             cfg.html = this.label;
22494         }
22495         
22496         if(this.panel){
22497             cfg.cls += ' progress-bar-' + this.panel;
22498         }
22499         
22500         return cfg;
22501     },
22502     
22503     update : function(aria_valuenow)
22504     {
22505         this.aria_valuenow = aria_valuenow;
22506         
22507         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22508     }
22509    
22510 });
22511
22512  
22513
22514  /**
22515  * @class Roo.bootstrap.TabGroup
22516  * @extends Roo.bootstrap.Column
22517  * @children Roo.bootstrap.TabPanel
22518  * Bootstrap Column class
22519  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22520  * @cfg {Boolean} carousel true to make the group behave like a carousel
22521  * @cfg {Boolean} bullets show bullets for the panels
22522  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22523  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22524  * @cfg {Boolean} showarrow (true|false) show arrow default true
22525  * 
22526  * @constructor
22527  * Create a new TabGroup
22528  * @param {Object} config The config object
22529  */
22530
22531 Roo.bootstrap.TabGroup = function(config){
22532     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22533     if (!this.navId) {
22534         this.navId = Roo.id();
22535     }
22536     this.tabs = [];
22537     Roo.bootstrap.TabGroup.register(this);
22538     
22539 };
22540
22541 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22542     
22543     carousel : false,
22544     transition : false,
22545     bullets : 0,
22546     timer : 0,
22547     autoslide : false,
22548     slideFn : false,
22549     slideOnTouch : false,
22550     showarrow : true,
22551     
22552     getAutoCreate : function()
22553     {
22554         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22555         
22556         cfg.cls += ' tab-content';
22557         
22558         if (this.carousel) {
22559             cfg.cls += ' carousel slide';
22560             
22561             cfg.cn = [{
22562                cls : 'carousel-inner',
22563                cn : []
22564             }];
22565         
22566             if(this.bullets  && !Roo.isTouch){
22567                 
22568                 var bullets = {
22569                     cls : 'carousel-bullets',
22570                     cn : []
22571                 };
22572                
22573                 if(this.bullets_cls){
22574                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22575                 }
22576                 
22577                 bullets.cn.push({
22578                     cls : 'clear'
22579                 });
22580                 
22581                 cfg.cn[0].cn.push(bullets);
22582             }
22583             
22584             if(this.showarrow){
22585                 cfg.cn[0].cn.push({
22586                     tag : 'div',
22587                     class : 'carousel-arrow',
22588                     cn : [
22589                         {
22590                             tag : 'div',
22591                             class : 'carousel-prev',
22592                             cn : [
22593                                 {
22594                                     tag : 'i',
22595                                     class : 'fa fa-chevron-left'
22596                                 }
22597                             ]
22598                         },
22599                         {
22600                             tag : 'div',
22601                             class : 'carousel-next',
22602                             cn : [
22603                                 {
22604                                     tag : 'i',
22605                                     class : 'fa fa-chevron-right'
22606                                 }
22607                             ]
22608                         }
22609                     ]
22610                 });
22611             }
22612             
22613         }
22614         
22615         return cfg;
22616     },
22617     
22618     initEvents:  function()
22619     {
22620 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22621 //            this.el.on("touchstart", this.onTouchStart, this);
22622 //        }
22623         
22624         if(this.autoslide){
22625             var _this = this;
22626             
22627             this.slideFn = window.setInterval(function() {
22628                 _this.showPanelNext();
22629             }, this.timer);
22630         }
22631         
22632         if(this.showarrow){
22633             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22634             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22635         }
22636         
22637         
22638     },
22639     
22640 //    onTouchStart : function(e, el, o)
22641 //    {
22642 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22643 //            return;
22644 //        }
22645 //        
22646 //        this.showPanelNext();
22647 //    },
22648     
22649     
22650     getChildContainer : function()
22651     {
22652         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22653     },
22654     
22655     /**
22656     * register a Navigation item
22657     * @param {Roo.bootstrap.nav.Item} the navitem to add
22658     */
22659     register : function(item)
22660     {
22661         this.tabs.push( item);
22662         item.navId = this.navId; // not really needed..
22663         this.addBullet();
22664     
22665     },
22666     
22667     getActivePanel : function()
22668     {
22669         var r = false;
22670         Roo.each(this.tabs, function(t) {
22671             if (t.active) {
22672                 r = t;
22673                 return false;
22674             }
22675             return null;
22676         });
22677         return r;
22678         
22679     },
22680     getPanelByName : function(n)
22681     {
22682         var r = false;
22683         Roo.each(this.tabs, function(t) {
22684             if (t.tabId == n) {
22685                 r = t;
22686                 return false;
22687             }
22688             return null;
22689         });
22690         return r;
22691     },
22692     indexOfPanel : function(p)
22693     {
22694         var r = false;
22695         Roo.each(this.tabs, function(t,i) {
22696             if (t.tabId == p.tabId) {
22697                 r = i;
22698                 return false;
22699             }
22700             return null;
22701         });
22702         return r;
22703     },
22704     /**
22705      * show a specific panel
22706      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22707      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22708      */
22709     showPanel : function (pan)
22710     {
22711         if(this.transition || typeof(pan) == 'undefined'){
22712             Roo.log("waiting for the transitionend");
22713             return false;
22714         }
22715         
22716         if (typeof(pan) == 'number') {
22717             pan = this.tabs[pan];
22718         }
22719         
22720         if (typeof(pan) == 'string') {
22721             pan = this.getPanelByName(pan);
22722         }
22723         
22724         var cur = this.getActivePanel();
22725         
22726         if(!pan || !cur){
22727             Roo.log('pan or acitve pan is undefined');
22728             return false;
22729         }
22730         
22731         if (pan.tabId == this.getActivePanel().tabId) {
22732             return true;
22733         }
22734         
22735         if (false === cur.fireEvent('beforedeactivate')) {
22736             return false;
22737         }
22738         
22739         if(this.bullets > 0 && !Roo.isTouch){
22740             this.setActiveBullet(this.indexOfPanel(pan));
22741         }
22742         
22743         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22744             
22745             //class="carousel-item carousel-item-next carousel-item-left"
22746             
22747             this.transition = true;
22748             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22749             var lr = dir == 'next' ? 'left' : 'right';
22750             pan.el.addClass(dir); // or prev
22751             pan.el.addClass('carousel-item-' + dir); // or prev
22752             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22753             cur.el.addClass(lr); // or right
22754             pan.el.addClass(lr);
22755             cur.el.addClass('carousel-item-' +lr); // or right
22756             pan.el.addClass('carousel-item-' +lr);
22757             
22758             
22759             var _this = this;
22760             cur.el.on('transitionend', function() {
22761                 Roo.log("trans end?");
22762                 
22763                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22764                 pan.setActive(true);
22765                 
22766                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22767                 cur.setActive(false);
22768                 
22769                 _this.transition = false;
22770                 
22771             }, this, { single:  true } );
22772             
22773             return true;
22774         }
22775         
22776         cur.setActive(false);
22777         pan.setActive(true);
22778         
22779         return true;
22780         
22781     },
22782     showPanelNext : function()
22783     {
22784         var i = this.indexOfPanel(this.getActivePanel());
22785         
22786         if (i >= this.tabs.length - 1 && !this.autoslide) {
22787             return;
22788         }
22789         
22790         if (i >= this.tabs.length - 1 && this.autoslide) {
22791             i = -1;
22792         }
22793         
22794         this.showPanel(this.tabs[i+1]);
22795     },
22796     
22797     showPanelPrev : function()
22798     {
22799         var i = this.indexOfPanel(this.getActivePanel());
22800         
22801         if (i  < 1 && !this.autoslide) {
22802             return;
22803         }
22804         
22805         if (i < 1 && this.autoslide) {
22806             i = this.tabs.length;
22807         }
22808         
22809         this.showPanel(this.tabs[i-1]);
22810     },
22811     
22812     
22813     addBullet: function()
22814     {
22815         if(!this.bullets || Roo.isTouch){
22816             return;
22817         }
22818         var ctr = this.el.select('.carousel-bullets',true).first();
22819         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22820         var bullet = ctr.createChild({
22821             cls : 'bullet bullet-' + i
22822         },ctr.dom.lastChild);
22823         
22824         
22825         var _this = this;
22826         
22827         bullet.on('click', (function(e, el, o, ii, t){
22828
22829             e.preventDefault();
22830
22831             this.showPanel(ii);
22832
22833             if(this.autoslide && this.slideFn){
22834                 clearInterval(this.slideFn);
22835                 this.slideFn = window.setInterval(function() {
22836                     _this.showPanelNext();
22837                 }, this.timer);
22838             }
22839
22840         }).createDelegate(this, [i, bullet], true));
22841                 
22842         
22843     },
22844      
22845     setActiveBullet : function(i)
22846     {
22847         if(Roo.isTouch){
22848             return;
22849         }
22850         
22851         Roo.each(this.el.select('.bullet', true).elements, function(el){
22852             el.removeClass('selected');
22853         });
22854
22855         var bullet = this.el.select('.bullet-' + i, true).first();
22856         
22857         if(!bullet){
22858             return;
22859         }
22860         
22861         bullet.addClass('selected');
22862     }
22863     
22864     
22865   
22866 });
22867
22868  
22869
22870  
22871  
22872 Roo.apply(Roo.bootstrap.TabGroup, {
22873     
22874     groups: {},
22875      /**
22876     * register a Navigation Group
22877     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22878     */
22879     register : function(navgrp)
22880     {
22881         this.groups[navgrp.navId] = navgrp;
22882         
22883     },
22884     /**
22885     * fetch a Navigation Group based on the navigation ID
22886     * if one does not exist , it will get created.
22887     * @param {string} the navgroup to add
22888     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22889     */
22890     get: function(navId) {
22891         if (typeof(this.groups[navId]) == 'undefined') {
22892             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22893         }
22894         return this.groups[navId] ;
22895     }
22896     
22897     
22898     
22899 });
22900
22901  /*
22902  * - LGPL
22903  *
22904  * TabPanel
22905  * 
22906  */
22907
22908 /**
22909  * @class Roo.bootstrap.TabPanel
22910  * @extends Roo.bootstrap.Component
22911  * @children Roo.bootstrap.Component
22912  * Bootstrap TabPanel class
22913  * @cfg {Boolean} active panel active
22914  * @cfg {String} html panel content
22915  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22916  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22917  * @cfg {String} href click to link..
22918  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22919  * 
22920  * 
22921  * @constructor
22922  * Create a new TabPanel
22923  * @param {Object} config The config object
22924  */
22925
22926 Roo.bootstrap.TabPanel = function(config){
22927     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22928     this.addEvents({
22929         /**
22930              * @event changed
22931              * Fires when the active status changes
22932              * @param {Roo.bootstrap.TabPanel} this
22933              * @param {Boolean} state the new state
22934             
22935          */
22936         'changed': true,
22937         /**
22938              * @event beforedeactivate
22939              * Fires before a tab is de-activated - can be used to do validation on a form.
22940              * @param {Roo.bootstrap.TabPanel} this
22941              * @return {Boolean} false if there is an error
22942             
22943          */
22944         'beforedeactivate': true
22945      });
22946     
22947     this.tabId = this.tabId || Roo.id();
22948   
22949 };
22950
22951 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22952     
22953     active: false,
22954     html: false,
22955     tabId: false,
22956     navId : false,
22957     href : '',
22958     touchSlide : false,
22959     getAutoCreate : function(){
22960         
22961         
22962         var cfg = {
22963             tag: 'div',
22964             // item is needed for carousel - not sure if it has any effect otherwise
22965             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22966             html: this.html || ''
22967         };
22968         
22969         if(this.active){
22970             cfg.cls += ' active';
22971         }
22972         
22973         if(this.tabId){
22974             cfg.tabId = this.tabId;
22975         }
22976         
22977         
22978         
22979         return cfg;
22980     },
22981     
22982     initEvents:  function()
22983     {
22984         var p = this.parent();
22985         
22986         this.navId = this.navId || p.navId;
22987         
22988         if (typeof(this.navId) != 'undefined') {
22989             // not really needed.. but just in case.. parent should be a NavGroup.
22990             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22991             
22992             tg.register(this);
22993             
22994             var i = tg.tabs.length - 1;
22995             
22996             if(this.active && tg.bullets > 0 && i < tg.bullets){
22997                 tg.setActiveBullet(i);
22998             }
22999         }
23000         
23001         this.el.on('click', this.onClick, this);
23002         
23003         if(Roo.isTouch && this.touchSlide){
23004             this.el.on("touchstart", this.onTouchStart, this);
23005             this.el.on("touchmove", this.onTouchMove, this);
23006             this.el.on("touchend", this.onTouchEnd, this);
23007         }
23008         
23009     },
23010     
23011     onRender : function(ct, position)
23012     {
23013         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
23014     },
23015     
23016     setActive : function(state)
23017     {
23018         Roo.log("panel - set active " + this.tabId + "=" + state);
23019         
23020         this.active = state;
23021         if (!state) {
23022             this.el.removeClass('active');
23023             
23024         } else  if (!this.el.hasClass('active')) {
23025             this.el.addClass('active');
23026         }
23027         
23028         this.fireEvent('changed', this, state);
23029     },
23030     
23031     onClick : function(e)
23032     {
23033         e.preventDefault();
23034         
23035         if(!this.href.length){
23036             return;
23037         }
23038         
23039         window.location.href = this.href;
23040     },
23041     
23042     startX : 0,
23043     startY : 0,
23044     endX : 0,
23045     endY : 0,
23046     swiping : false,
23047     
23048     onTouchStart : function(e)
23049     {
23050         this.swiping = false;
23051         
23052         this.startX = e.browserEvent.touches[0].clientX;
23053         this.startY = e.browserEvent.touches[0].clientY;
23054     },
23055     
23056     onTouchMove : function(e)
23057     {
23058         this.swiping = true;
23059         
23060         this.endX = e.browserEvent.touches[0].clientX;
23061         this.endY = e.browserEvent.touches[0].clientY;
23062     },
23063     
23064     onTouchEnd : function(e)
23065     {
23066         if(!this.swiping){
23067             this.onClick(e);
23068             return;
23069         }
23070         
23071         var tabGroup = this.parent();
23072         
23073         if(this.endX > this.startX){ // swiping right
23074             tabGroup.showPanelPrev();
23075             return;
23076         }
23077         
23078         if(this.startX > this.endX){ // swiping left
23079             tabGroup.showPanelNext();
23080             return;
23081         }
23082     }
23083     
23084     
23085 });
23086  
23087
23088  
23089
23090  /*
23091  * - LGPL
23092  *
23093  * DateField
23094  * 
23095  */
23096
23097 /**
23098  * @class Roo.bootstrap.form.DateField
23099  * @extends Roo.bootstrap.form.Input
23100  * Bootstrap DateField class
23101  * @cfg {Number} weekStart default 0
23102  * @cfg {String} viewMode default empty, (months|years)
23103  * @cfg {String} minViewMode default empty, (months|years)
23104  * @cfg {Number} startDate default -Infinity
23105  * @cfg {Number} endDate default Infinity
23106  * @cfg {Boolean} todayHighlight default false
23107  * @cfg {Boolean} todayBtn default false
23108  * @cfg {Boolean} calendarWeeks default false
23109  * @cfg {Object} daysOfWeekDisabled default empty
23110  * @cfg {Boolean} singleMode default false (true | false)
23111  * 
23112  * @cfg {Boolean} keyboardNavigation default true
23113  * @cfg {String} language default en
23114  * 
23115  * @constructor
23116  * Create a new DateField
23117  * @param {Object} config The config object
23118  */
23119  
23120 Roo.bootstrap.form.DateField = function(config){
23121     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
23122      this.addEvents({
23123             /**
23124              * @event show
23125              * Fires when this field show.
23126              * @param {Roo.bootstrap.form.DateField} this
23127              * @param {Mixed} date The date value
23128              */
23129             show : true,
23130             /**
23131              * @event show
23132              * Fires when this field hide.
23133              * @param {Roo.bootstrap.form.DateField} this
23134              * @param {Mixed} date The date value
23135              */
23136             hide : true,
23137             /**
23138              * @event select
23139              * Fires when select a date.
23140              * @param {Roo.bootstrap.form.DateField} this
23141              * @param {Mixed} date The date value
23142              */
23143             select : true,
23144             /**
23145              * @event beforeselect
23146              * Fires when before select a date.
23147              * @param {Roo.bootstrap.form.DateField} this
23148              * @param {Mixed} date The date value
23149              */
23150             beforeselect : true
23151         });
23152 };
23153
23154 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
23155     
23156     /**
23157      * @cfg {String} format
23158      * The default date format string which can be overriden for localization support.  The format must be
23159      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23160      */
23161     format : "m/d/y",
23162     
23163     weekStart : 0,
23164     
23165     viewMode : '',
23166     
23167     minViewMode : '',
23168     
23169     todayHighlight : false,
23170     
23171     todayBtn: false,
23172     
23173     language: 'en',
23174     
23175     keyboardNavigation: true,
23176     
23177     calendarWeeks: false,
23178     
23179     startDate: -Infinity,
23180     
23181     endDate: Infinity,
23182     
23183     daysOfWeekDisabled: [],
23184     
23185     _events: [],
23186     
23187     singleMode : false,
23188
23189     hiddenField : false,
23190     
23191     UTCDate: function()
23192     {
23193         return new Date(Date.UTC.apply(Date, arguments));
23194     },
23195     
23196     UTCToday: function()
23197     {
23198         var today = new Date();
23199         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
23200     },
23201     
23202     getDate: function() {
23203             var d = this.getUTCDate();
23204             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
23205     },
23206     
23207     getUTCDate: function() {
23208             return this.date;
23209     },
23210     
23211     setDate: function(d) {
23212             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
23213     },
23214     
23215     setUTCDate: function(d) {
23216             this.date = d;
23217             this.setValue(this.date);
23218     },
23219
23220     translateDates: function(lang) 
23221     {
23222         var translation = Roo.bootstrap.form.DateField.dates[lang] = {
23223             days: [],
23224             daysShort: [],
23225             daysMin: [],
23226             months: [],
23227             monthsShort: []
23228         };
23229
23230         var locale = lang.replace('_', '-');
23231
23232         var is_latin = [ 'zh-hk', 'zh-cn', 'jp', 'ko' ].indexOf(locale.toLowerCase()) < 0; 
23233                  
23234
23235         // fill days
23236         for(var i = 0; i < 7; i++) {
23237             var date = new Date(2020, 0, 5 + i);
23238
23239             var day = new Intl.DateTimeFormat(locale, {
23240                 weekday : 'long'
23241             }).format(date);
23242
23243             var dayShort = new Intl.DateTimeFormat(locale, {
23244                 weekday : 'short'
23245             }).format(date);
23246
23247             var dayMin = new Intl.DateTimeFormat(locale, {
23248                 weekday : 'narrow'
23249             }).format(date);
23250
23251             if(is_latin) {
23252                 dayShort = day.substring(0, 3);
23253                 dayMin = day.substring(0, 2);
23254             }
23255             
23256             translation.days.push(day);
23257             translation.daysShort.push(dayShort);
23258             translation.daysMin.push(dayMin);
23259         }
23260
23261         // fill months
23262         for(var i = 0; i < 12; i++) {
23263             var date = new Date(2020, i);
23264
23265             var month = new Intl.DateTimeFormat(locale, {
23266                 month : 'long'
23267             }).format(date);
23268
23269             var monthShort = new Intl.DateTimeFormat(locale, {
23270                 month : 'short'
23271             }).format(date);
23272
23273             if(is_latin) {
23274                 monthShort = month.substring(0, 3);
23275             }
23276
23277             translation.months.push(month);
23278             translation.monthsShort.push(monthShort);
23279         }
23280     },
23281         
23282     onRender: function(ct, position)
23283     {
23284         
23285         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
23286
23287         this.translateDates(this.language);
23288         
23289         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
23290         this.format = this.format || 'm/d/y';
23291         this.isInline = false;
23292         this.isInput = true;
23293         this.component = this.el.select('.add-on', true).first() || false;
23294         this.component = (this.component && this.component.length === 0) ? false : this.component;
23295         this.hasInput = this.component && this.inputEl().length;
23296         
23297         if (typeof(this.minViewMode === 'string')) {
23298             switch (this.minViewMode) {
23299                 case 'months':
23300                     this.minViewMode = 1;
23301                     break;
23302                 case 'years':
23303                     this.minViewMode = 2;
23304                     break;
23305                 default:
23306                     this.minViewMode = 0;
23307                     break;
23308             }
23309         }
23310         
23311         if (typeof(this.viewMode === 'string')) {
23312             switch (this.viewMode) {
23313                 case 'months':
23314                     this.viewMode = 1;
23315                     break;
23316                 case 'years':
23317                     this.viewMode = 2;
23318                     break;
23319                 default:
23320                     this.viewMode = 0;
23321                     break;
23322             }
23323         }
23324                 
23325         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23326         
23327 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23328         
23329         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23330         
23331         this.picker().on('mousedown', this.onMousedown, this);
23332         this.picker().on('click', this.onClick, this);
23333         
23334         this.picker().addClass('datepicker-dropdown');
23335         
23336         this.startViewMode = this.viewMode;
23337         
23338         if(this.singleMode){
23339             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23340                 v.setVisibilityMode(Roo.Element.DISPLAY);
23341                 v.hide();
23342             });
23343             
23344             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23345                 v.setStyle('width', '189px');
23346             });
23347         }
23348         
23349         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23350             v.dom.innerHTML = Roo.bootstrap.form.DateField.todayText;
23351         });
23352                         
23353         
23354         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23355         
23356         this.setStartDate(this.startDate);
23357         this.setEndDate(this.endDate);
23358         
23359         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23360         
23361         this.fillDow();
23362         this.fillMonths();
23363         this.update();
23364         this.showMode();
23365         
23366         if(this.isInline) {
23367             this.showPopup();
23368         }
23369
23370         this.hiddenField = this.inputEl().insertSibling(
23371             {tag : 'input', type : 'hidden', name : this.name},
23372             'before',
23373             true
23374         );
23375         this.inputEl().dom.setAttribute('name', this.name + '____hidden___');
23376
23377     },
23378     
23379     picker : function()
23380     {
23381         return this.pickerEl;
23382 //        return this.el.select('.datepicker', true).first();
23383     },
23384     
23385     fillDow: function()
23386     {
23387         var dowCnt = this.weekStart;
23388         
23389         var dow = {
23390             tag: 'tr',
23391             cn: [
23392                 
23393             ]
23394         };
23395         
23396         while (dowCnt < this.weekStart + 7) {
23397             dow.cn.push({
23398                 tag: 'th',
23399                 cls: 'dow',
23400                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23401             });
23402         }
23403         
23404         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23405     },
23406     
23407     fillMonths: function()
23408     {    
23409         var i = 0;
23410         var months = this.picker().select('>.datepicker-months td', true).first();
23411         
23412         months.dom.innerHTML = '';
23413         
23414         while (i < 12) {
23415             var month = {
23416                 tag: 'span',
23417                 cls: 'month',
23418                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23419             };
23420             
23421             months.createChild(month);
23422         }
23423         
23424     },
23425     
23426     update: function()
23427     {
23428         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;
23429         
23430         if (this.date < this.startDate) {
23431             this.viewDate = new Date(this.startDate);
23432         } else if (this.date > this.endDate) {
23433             this.viewDate = new Date(this.endDate);
23434         } else {
23435             this.viewDate = new Date(this.date);
23436         }
23437         
23438         this.fill();
23439     },
23440     
23441     fill: function() 
23442     {
23443         var d = new Date(this.viewDate),
23444                 year = d.getUTCFullYear(),
23445                 month = d.getUTCMonth(),
23446                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23447                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23448                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23449                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23450                 currentDate = this.date && this.date.valueOf(),
23451                 today = this.UTCToday();
23452         
23453         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23454     
23455         this.updateNavArrows();
23456         this.fillMonths();
23457                                                 
23458         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23459         
23460         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23461          
23462         prevMonth.setUTCDate(day);
23463         
23464         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23465         
23466         var nextMonth = new Date(prevMonth);
23467         
23468         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23469         
23470         nextMonth = nextMonth.valueOf();
23471         
23472         var fillMonths = false;
23473         
23474         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23475         
23476         while(prevMonth.valueOf() <= nextMonth) {
23477             var clsName = '';
23478             
23479             if (prevMonth.getUTCDay() === this.weekStart) {
23480                 if(fillMonths){
23481                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23482                 }
23483                     
23484                 fillMonths = {
23485                     tag: 'tr',
23486                     cn: []
23487                 };
23488             }
23489             
23490             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23491                 clsName += ' old';
23492             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23493                 clsName += ' new';
23494             }
23495             if (this.todayHighlight &&
23496                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23497                 prevMonth.getUTCMonth() == today.getMonth() &&
23498                 prevMonth.getUTCDate() == today.getDate()) {
23499                 clsName += ' today';
23500             }
23501             
23502             if (currentDate && prevMonth.valueOf() === currentDate) {
23503                 clsName += ' active';
23504             }
23505             
23506             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23507                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23508                     clsName += ' disabled';
23509             }
23510             
23511             fillMonths.cn.push({
23512                 tag: 'td',
23513                 cls: 'day ' + clsName,
23514                 html: prevMonth.getDate()
23515             });
23516             
23517             prevMonth.setDate(prevMonth.getDate()+1);
23518         }
23519           
23520         var currentYear = this.date && this.date.getUTCFullYear();
23521         var currentMonth = this.date && this.date.getUTCMonth();
23522         
23523         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23524         
23525         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23526             v.removeClass('active');
23527             
23528             if(currentYear === year && k === currentMonth){
23529                 v.addClass('active');
23530             }
23531             
23532             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23533                 v.addClass('disabled');
23534             }
23535             
23536         });
23537         
23538         
23539         year = parseInt(year/10, 10) * 10;
23540         
23541         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23542         
23543         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23544         
23545         year -= 1;
23546         for (var i = -1; i < 11; i++) {
23547             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23548                 tag: 'span',
23549                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23550                 html: year
23551             });
23552             
23553             year += 1;
23554         }
23555     },
23556     
23557     showMode: function(dir) 
23558     {
23559         if (dir) {
23560             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23561         }
23562         
23563         Roo.each(this.picker().select('>div',true).elements, function(v){
23564             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23565             v.hide();
23566         });
23567         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23568     },
23569     
23570     place: function()
23571     {
23572         if(this.isInline) {
23573             return;
23574         }
23575         
23576         this.picker().removeClass(['bottom', 'top']);
23577         
23578         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23579             /*
23580              * place to the top of element!
23581              *
23582              */
23583             
23584             this.picker().addClass('top');
23585             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23586             
23587             return;
23588         }
23589         
23590         this.picker().addClass('bottom');
23591         
23592         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23593     },
23594     
23595     // return false when it fails
23596     parseDate : function(value)
23597     {
23598         if(!value) {
23599             return false;
23600         }
23601         if(value instanceof Date){
23602             return value;
23603         }
23604         var v = Date.parseDate(value, 'Y-m-d');
23605
23606         return (typeof(v) == 'undefined') ? false : v;
23607     },
23608     
23609     formatDate : function(date, fmt)
23610     {   
23611         return (!date || !(date instanceof Date)) ?
23612         date : date.dateFormat(fmt || this.format);
23613     },
23614
23615     translateDate : function(date)
23616     {
23617         switch(this.language) {
23618             case 'zh_CN':
23619                 return new Intl.DateTimeFormat('zh-CN', {
23620                     year : 'numeric',
23621                     month : 'long',
23622                     day : 'numeric'
23623                 }).format(date);
23624             default :
23625                 return this.formatDate(date);
23626         }
23627     },
23628     
23629     onFocus : function()
23630     {
23631         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23632         this.showPopup();
23633     },
23634     
23635     onBlur : function()
23636     {
23637         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23638
23639         if(!this.readOnly) {
23640             var d = this.inputEl().getValue();
23641             var date = this.parseDate(d);
23642             if(date) {
23643                 this.setValue(date);
23644             }
23645             else {
23646                 this.setValue(this.getValue());
23647             }
23648         }
23649                 
23650         this.hidePopup();
23651     },
23652     
23653     showPopup : function()
23654     {
23655         this.picker().show();
23656         this.update();
23657         this.place();
23658         
23659         this.fireEvent('showpopup', this, this.date);
23660     },
23661     
23662     hidePopup : function()
23663     {
23664         if(this.isInline) {
23665             return;
23666         }
23667         this.picker().hide();
23668         this.viewMode = this.startViewMode;
23669         this.showMode();
23670         
23671         this.fireEvent('hidepopup', this, this.date);
23672         
23673     },
23674     
23675     onMousedown: function(e)
23676     {
23677         e.stopPropagation();
23678         e.preventDefault();
23679     },
23680     
23681     keyup: function(e)
23682     {
23683         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23684         this.update();
23685     },
23686
23687     setValue: function(v)
23688     {
23689         if(this.fireEvent('beforeselect', this, v) !== false){
23690             var d = this.parseDate(v);
23691
23692             if(!d) {
23693                 this.date = this.viewDate = this.value = this.hiddenField.value =  '';
23694                 if(this.rendered){
23695                     this.inputEl().dom.value = '';
23696                     this.validate();
23697                 }
23698                 return;
23699             }
23700
23701             d = new Date(d).clearTime();
23702
23703             this.value = this.hiddenField.value = d.dateFormat('Y-m-d');
23704
23705             v = this.translateDate(d);
23706             if(this.rendered){
23707                 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
23708                 this.validate();
23709             }
23710
23711             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23712
23713             this.update();
23714
23715             this.fireEvent('select', this, this.date);
23716         }
23717     },
23718
23719     // bypass validation
23720     setRawValue : function(v){
23721         if(this.fireEvent('beforeselect', this, v) !== false){
23722             var d = this.parseDate(v);
23723
23724             if(!d) {
23725                 this.date = this.viewDate = this.value = this.hiddenField.value =  '';
23726                 if(this.rendered){
23727                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
23728                 }
23729                 return;
23730             }
23731
23732             d = new Date(d).clearTime();
23733
23734             this.value = this.hiddenField.value = d.dateFormat('Y-m-d');
23735
23736             v = this.translateDate(d);
23737             if(this.rendered){
23738                 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
23739             }
23740
23741             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23742
23743             this.update();
23744
23745             this.fireEvent('select', this, this.date);
23746         }
23747     },
23748     
23749     getValue: function()
23750     {
23751         return this.value;
23752     },
23753
23754     getRawValue : function(){
23755         return this.getValue();
23756     },
23757     
23758     fireKey: function(e)
23759     {
23760         if (!this.picker().isVisible()){
23761             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23762                 this.showPopup();
23763             }
23764             return;
23765         }
23766         
23767         var dateChanged = false,
23768         dir, day, month,
23769         newDate, newViewDate;
23770         
23771         switch(e.keyCode){
23772             case 27: // escape
23773                 this.hidePopup();
23774                 e.preventDefault();
23775                 break;
23776             case 37: // left
23777             case 39: // right
23778                 if (!this.keyboardNavigation) {
23779                     break;
23780                 }
23781                 dir = e.keyCode == 37 ? -1 : 1;
23782                 
23783                 if (e.ctrlKey){
23784                     newDate = this.moveYear(this.date, dir);
23785                     newViewDate = this.moveYear(this.viewDate, dir);
23786                 } else if (e.shiftKey){
23787                     newDate = this.moveMonth(this.date, dir);
23788                     newViewDate = this.moveMonth(this.viewDate, dir);
23789                 } else {
23790                     newDate = new Date(this.date);
23791                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23792                     newViewDate = new Date(this.viewDate);
23793                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23794                 }
23795                 if (this.dateWithinRange(newDate)){
23796                     this.date = newDate;
23797                     this.viewDate = newViewDate;
23798                     this.setValue(this.date);
23799 //                    this.update();
23800                     e.preventDefault();
23801                     dateChanged = true;
23802                 }
23803                 break;
23804             case 38: // up
23805             case 40: // down
23806                 if (!this.keyboardNavigation) {
23807                     break;
23808                 }
23809                 dir = e.keyCode == 38 ? -1 : 1;
23810                 if (e.ctrlKey){
23811                     newDate = this.moveYear(this.date, dir);
23812                     newViewDate = this.moveYear(this.viewDate, dir);
23813                 } else if (e.shiftKey){
23814                     newDate = this.moveMonth(this.date, dir);
23815                     newViewDate = this.moveMonth(this.viewDate, dir);
23816                 } else {
23817                     newDate = new Date(this.date);
23818                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23819                     newViewDate = new Date(this.viewDate);
23820                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23821                 }
23822                 if (this.dateWithinRange(newDate)){
23823                     this.date = newDate;
23824                     this.viewDate = newViewDate;
23825                     this.setValue(this.date);
23826 //                    this.update();
23827                     e.preventDefault();
23828                     dateChanged = true;
23829                 }
23830                 break;
23831             case 13: // enter
23832                 this.setValue(this.date);
23833                 this.hidePopup();
23834                 e.preventDefault();
23835                 break;
23836             case 9: // tab
23837                 this.setValue(this.date);
23838                 this.hidePopup();
23839                 break;
23840             case 16: // shift
23841             case 17: // ctrl
23842             case 18: // alt
23843                 break;
23844             default :
23845                 this.hidePopup();
23846                 
23847         }
23848     },
23849     
23850     
23851     onClick: function(e) 
23852     {
23853         e.stopPropagation();
23854         e.preventDefault();
23855         
23856         var target = e.getTarget();
23857         
23858         if(target.nodeName.toLowerCase() === 'i'){
23859             target = Roo.get(target).dom.parentNode;
23860         }
23861         
23862         var nodeName = target.nodeName;
23863         var className = target.className;
23864         var html = target.innerHTML;
23865         //Roo.log(nodeName);
23866         
23867         switch(nodeName.toLowerCase()) {
23868             case 'th':
23869                 switch(className) {
23870                     case 'switch':
23871                         this.showMode(1);
23872                         break;
23873                     case 'prev':
23874                     case 'next':
23875                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23876                         switch(this.viewMode){
23877                                 case 0:
23878                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23879                                         break;
23880                                 case 1:
23881                                 case 2:
23882                                         this.viewDate = this.moveYear(this.viewDate, dir);
23883                                         break;
23884                         }
23885                         this.fill();
23886                         break;
23887                     case 'today':
23888                         var date = new Date();
23889                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23890 //                        this.fill()
23891                         this.setValue(this.date);
23892                         
23893                         this.hidePopup();
23894                         break;
23895                 }
23896                 break;
23897             case 'span':
23898                 if (className.indexOf('disabled') < 0) {
23899                 if (!this.viewDate) {
23900                     this.viewDate = new Date();
23901                 }
23902                 this.viewDate.setUTCDate(1);
23903                     if (className.indexOf('month') > -1) {
23904                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23905                     } else {
23906                         var year = parseInt(html, 10) || 0;
23907                         this.viewDate.setUTCFullYear(year);
23908                         
23909                     }
23910                     
23911                     if(this.singleMode){
23912                         this.setValue(this.viewDate);
23913                         this.hidePopup();
23914                         return;
23915                     }
23916                     
23917                     this.showMode(-1);
23918                     this.fill();
23919                 }
23920                 break;
23921                 
23922             case 'td':
23923                 //Roo.log(className);
23924                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23925                     var day = parseInt(html, 10) || 1;
23926                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23927                         month = (this.viewDate || new Date()).getUTCMonth();
23928
23929                     if (className.indexOf('old') > -1) {
23930                         if(month === 0 ){
23931                             month = 11;
23932                             year -= 1;
23933                         }else{
23934                             month -= 1;
23935                         }
23936                     } else if (className.indexOf('new') > -1) {
23937                         if (month == 11) {
23938                             month = 0;
23939                             year += 1;
23940                         } else {
23941                             month += 1;
23942                         }
23943                     }
23944                     //Roo.log([year,month,day]);
23945                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23946                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23947 //                    this.fill();
23948                     this.setValue(this.date);
23949                     this.hidePopup();
23950                 }
23951                 break;
23952         }
23953     },
23954     
23955     setStartDate: function(startDate)
23956     {
23957         this.startDate = startDate || -Infinity;
23958         if (this.startDate !== -Infinity) {
23959             var date = this.parseDate(this.startDate);
23960             this.startDate = date ? date : -Infinity;
23961         }
23962         this.update();
23963         this.updateNavArrows();
23964     },
23965
23966     setEndDate: function(endDate)
23967     {
23968         this.endDate = endDate || Infinity;
23969         if (this.endDate !== Infinity) {
23970             var date = this.parseDate(this.endDate);
23971             this.endDate = date ? date : Infinity;
23972         }
23973         this.update();
23974         this.updateNavArrows();
23975     },
23976     
23977     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23978     {
23979         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23980         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23981             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23982         }
23983         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23984             return parseInt(d, 10);
23985         });
23986         this.update();
23987         this.updateNavArrows();
23988     },
23989     
23990     updateNavArrows: function() 
23991     {
23992         if(this.singleMode){
23993             return;
23994         }
23995         
23996         var d = new Date(this.viewDate),
23997         year = d.getUTCFullYear(),
23998         month = d.getUTCMonth();
23999         
24000         Roo.each(this.picker().select('.prev', true).elements, function(v){
24001             v.show();
24002             switch (this.viewMode) {
24003                 case 0:
24004
24005                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
24006                         v.hide();
24007                     }
24008                     break;
24009                 case 1:
24010                 case 2:
24011                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
24012                         v.hide();
24013                     }
24014                     break;
24015             }
24016         });
24017         
24018         Roo.each(this.picker().select('.next', true).elements, function(v){
24019             v.show();
24020             switch (this.viewMode) {
24021                 case 0:
24022
24023                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
24024                         v.hide();
24025                     }
24026                     break;
24027                 case 1:
24028                 case 2:
24029                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
24030                         v.hide();
24031                     }
24032                     break;
24033             }
24034         })
24035     },
24036     
24037     moveMonth: function(date, dir)
24038     {
24039         if (!dir) {
24040             return date;
24041         }
24042         var new_date = new Date(date.valueOf()),
24043         day = new_date.getUTCDate(),
24044         month = new_date.getUTCMonth(),
24045         mag = Math.abs(dir),
24046         new_month, test;
24047         dir = dir > 0 ? 1 : -1;
24048         if (mag == 1){
24049             test = dir == -1
24050             // If going back one month, make sure month is not current month
24051             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
24052             ? function(){
24053                 return new_date.getUTCMonth() == month;
24054             }
24055             // If going forward one month, make sure month is as expected
24056             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
24057             : function(){
24058                 return new_date.getUTCMonth() != new_month;
24059             };
24060             new_month = month + dir;
24061             new_date.setUTCMonth(new_month);
24062             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
24063             if (new_month < 0 || new_month > 11) {
24064                 new_month = (new_month + 12) % 12;
24065             }
24066         } else {
24067             // For magnitudes >1, move one month at a time...
24068             for (var i=0; i<mag; i++) {
24069                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
24070                 new_date = this.moveMonth(new_date, dir);
24071             }
24072             // ...then reset the day, keeping it in the new month
24073             new_month = new_date.getUTCMonth();
24074             new_date.setUTCDate(day);
24075             test = function(){
24076                 return new_month != new_date.getUTCMonth();
24077             };
24078         }
24079         // Common date-resetting loop -- if date is beyond end of month, make it
24080         // end of month
24081         while (test()){
24082             new_date.setUTCDate(--day);
24083             new_date.setUTCMonth(new_month);
24084         }
24085         return new_date;
24086     },
24087
24088     moveYear: function(date, dir)
24089     {
24090         return this.moveMonth(date, dir*12);
24091     },
24092
24093     dateWithinRange: function(date)
24094     {
24095         return date >= this.startDate && date <= this.endDate;
24096     },
24097
24098     
24099     remove: function() 
24100     {
24101         this.picker().remove();
24102     },
24103     
24104     validateValue : function(value)
24105     {
24106         if(this.getVisibilityEl().hasClass('hidden')){
24107             return true;
24108         }
24109         
24110         if(value.length < 1)  {
24111             if(this.allowBlank){
24112                 return true;
24113             }
24114             return false;
24115         }
24116         
24117         if(value.length < this.minLength){
24118             return false;
24119         }
24120         if(value.length > this.maxLength){
24121             return false;
24122         }
24123         if(this.vtype){
24124             var vt = Roo.form.VTypes;
24125             if(!vt[this.vtype](value, this)){
24126                 return false;
24127             }
24128         }
24129         if(typeof this.validator == "function"){
24130             var msg = this.validator(value);
24131             if(msg !== true){
24132                 return false;
24133             }
24134         }
24135         
24136         if(this.regex && !this.regex.test(value)){
24137             return false;
24138         }
24139         
24140         if(!this.parseDate(value)){
24141             return false;
24142         }
24143         
24144         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
24145             return false;
24146         }      
24147         
24148         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
24149             return false;
24150         } 
24151         
24152         
24153         return true;
24154     },
24155     
24156     reset : function()
24157     {
24158         this.date = this.viewDate = '';
24159         
24160         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
24161     }
24162    
24163 });
24164
24165 Roo.apply(Roo.bootstrap.form.DateField,  {
24166     
24167     head : {
24168         tag: 'thead',
24169         cn: [
24170         {
24171             tag: 'tr',
24172             cn: [
24173             {
24174                 tag: 'th',
24175                 cls: 'prev',
24176                 html: '<i class="fa fa-arrow-left"/>'
24177             },
24178             {
24179                 tag: 'th',
24180                 cls: 'switch',
24181                 colspan: '5'
24182             },
24183             {
24184                 tag: 'th',
24185                 cls: 'next',
24186                 html: '<i class="fa fa-arrow-right"/>'
24187             }
24188
24189             ]
24190         }
24191         ]
24192     },
24193     
24194     content : {
24195         tag: 'tbody',
24196         cn: [
24197         {
24198             tag: 'tr',
24199             cn: [
24200             {
24201                 tag: 'td',
24202                 colspan: '7'
24203             }
24204             ]
24205         }
24206         ]
24207     },
24208     
24209     footer : {
24210         tag: 'tfoot',
24211         cn: [
24212         {
24213             tag: 'tr',
24214             cn: [
24215             {
24216                 tag: 'th',
24217                 colspan: '7',
24218                 cls: 'today'
24219             }
24220                     
24221             ]
24222         }
24223         ]
24224     },
24225     
24226     dates : {},
24227
24228     todayText : "Today",
24229     
24230     modes: [
24231     {
24232         clsName: 'days',
24233         navFnc: 'Month',
24234         navStep: 1
24235     },
24236     {
24237         clsName: 'months',
24238         navFnc: 'FullYear',
24239         navStep: 1
24240     },
24241     {
24242         clsName: 'years',
24243         navFnc: 'FullYear',
24244         navStep: 10
24245     }]
24246 });
24247
24248 Roo.apply(Roo.bootstrap.form.DateField,  {
24249   
24250     template : {
24251         tag: 'div',
24252         cls: 'datepicker dropdown-menu roo-dynamic shadow',
24253         cn: [
24254         {
24255             tag: 'div',
24256             cls: 'datepicker-days',
24257             cn: [
24258             {
24259                 tag: 'table',
24260                 cls: 'table-condensed',
24261                 cn:[
24262                 Roo.bootstrap.form.DateField.head,
24263                 {
24264                     tag: 'tbody'
24265                 },
24266                 Roo.bootstrap.form.DateField.footer
24267                 ]
24268             }
24269             ]
24270         },
24271         {
24272             tag: 'div',
24273             cls: 'datepicker-months',
24274             cn: [
24275             {
24276                 tag: 'table',
24277                 cls: 'table-condensed',
24278                 cn:[
24279                 Roo.bootstrap.form.DateField.head,
24280                 Roo.bootstrap.form.DateField.content,
24281                 Roo.bootstrap.form.DateField.footer
24282                 ]
24283             }
24284             ]
24285         },
24286         {
24287             tag: 'div',
24288             cls: 'datepicker-years',
24289             cn: [
24290             {
24291                 tag: 'table',
24292                 cls: 'table-condensed',
24293                 cn:[
24294                 Roo.bootstrap.form.DateField.head,
24295                 Roo.bootstrap.form.DateField.content,
24296                 Roo.bootstrap.form.DateField.footer
24297                 ]
24298             }
24299             ]
24300         }
24301         ]
24302     }
24303 });
24304
24305  
24306
24307  /*
24308  * - LGPL
24309  *
24310  * TimeField
24311  * 
24312  */
24313
24314 /**
24315  * @class Roo.bootstrap.form.TimeField
24316  * @extends Roo.bootstrap.form.Input
24317  * Bootstrap DateField class
24318  * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
24319  * 
24320  * 
24321  * @constructor
24322  * Create a new TimeField
24323  * @param {Object} config The config object
24324  */
24325
24326 Roo.bootstrap.form.TimeField = function(config){
24327     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
24328     this.addEvents({
24329             /**
24330              * @event show
24331              * Fires when this field show.
24332              * @param {Roo.bootstrap.form.DateField} thisthis
24333              * @param {Mixed} date The date value
24334              */
24335             show : true,
24336             /**
24337              * @event show
24338              * Fires when this field hide.
24339              * @param {Roo.bootstrap.form.DateField} this
24340              * @param {Mixed} date The date value
24341              */
24342             hide : true,
24343             /**
24344              * @event select
24345              * Fires when select a date.
24346              * @param {Roo.bootstrap.form.DateField} this
24347              * @param {Mixed} date The date value
24348              */
24349             select : true
24350         });
24351 };
24352
24353 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
24354     
24355     /**
24356      * @cfg {String} format
24357      * The default time format string which can be overriden for localization support.  The format must be
24358      * valid according to {@link Date#parseDate} (defaults to 'H:i').
24359      */
24360     format : "H:i",
24361     minuteStep : 1,
24362     language : 'en',
24363     hiddenField : false,
24364     getAutoCreate : function()
24365     {
24366         this.after = '<i class="fa far fa-clock"></i>';
24367         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24368         
24369          
24370     },
24371     onRender: function(ct, position)
24372     {
24373         
24374         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24375
24376         this.language = this.language in Roo.bootstrap.form.TimeField.periodText ? this.language : "en";
24377                 
24378         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24379         
24380         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24381         
24382         this.pop = this.picker().select('>.datepicker-time',true).first();
24383         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24384         
24385         this.picker().on('mousedown', this.onMousedown, this);
24386         this.picker().on('click', this.onClick, this);
24387         
24388         this.picker().addClass('datepicker-dropdown');
24389     
24390         this.fillTime();
24391         this.update();
24392             
24393         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24394         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24395         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24396         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24397         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24398         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24399         this.pop.select('button.ok', true).first().dom.innerHTML = Roo.bootstrap.form.TimeField.okText;
24400
24401         this.hiddenField = this.inputEl().insertSibling(
24402             {tag : 'input', type : 'hidden', name : this.name},
24403             'before',
24404             true
24405         );
24406         this.inputEl().dom.setAttribute('name', this.name + '____hidden___');
24407
24408     },
24409     
24410     fireKey: function(e){
24411         if (!this.picker().isVisible()){
24412             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24413                 this.show();
24414             }
24415             return;
24416         }
24417
24418         e.preventDefault();
24419         
24420         switch(e.keyCode){
24421             case 27: // escape
24422                 this.hide();
24423                 break;
24424             case 37: // left
24425             case 39: // right
24426                 this.onTogglePeriod();
24427                 break;
24428             case 38: // up
24429                 this.onIncrementMinutes();
24430                 break;
24431             case 40: // down
24432                 this.onDecrementMinutes();
24433                 break;
24434             case 13: // enter
24435             case 9: // tab
24436                 this.setTime();
24437                 break;
24438         }
24439     },
24440     
24441     onClick: function(e) {
24442         e.stopPropagation();
24443         e.preventDefault();
24444     },
24445     
24446     picker : function()
24447     {
24448         return this.pickerEl;
24449     },
24450     
24451     fillTime: function()
24452     {    
24453         var time = this.pop.select('tbody', true).first();
24454         
24455         time.dom.innerHTML = '';
24456         
24457         time.createChild({
24458             tag: 'tr',
24459             cn: [
24460                 {
24461                     tag: 'td',
24462                     cn: [
24463                         {
24464                             tag: 'a',
24465                             href: '#',
24466                             cls: 'btn',
24467                             cn: [
24468                                 {
24469                                     tag: 'i',
24470                                     cls: 'hours-up fa fas fa-chevron-up'
24471                                 }
24472                             ]
24473                         } 
24474                     ]
24475                 },
24476                 {
24477                     tag: 'td',
24478                     cls: 'separator'
24479                 },
24480                 {
24481                     tag: 'td',
24482                     cn: [
24483                         {
24484                             tag: 'a',
24485                             href: '#',
24486                             cls: 'btn',
24487                             cn: [
24488                                 {
24489                                     tag: 'i',
24490                                     cls: 'minutes-up fa fas fa-chevron-up'
24491                                 }
24492                             ]
24493                         }
24494                     ]
24495                 },
24496                 {
24497                     tag: 'td',
24498                     cls: 'separator'
24499                 }
24500             ]
24501         });
24502         
24503         time.createChild({
24504             tag: 'tr',
24505             cn: [
24506                 {
24507                     tag: 'td',
24508                     cn: [
24509                         {
24510                             tag: 'span',
24511                             cls: 'timepicker-hour',
24512                             html: '00'
24513                         }  
24514                     ]
24515                 },
24516                 {
24517                     tag: 'td',
24518                     cls: 'separator',
24519                     html: ':'
24520                 },
24521                 {
24522                     tag: 'td',
24523                     cn: [
24524                         {
24525                             tag: 'span',
24526                             cls: 'timepicker-minute',
24527                             html: '00'
24528                         }  
24529                     ]
24530                 },
24531                 {
24532                     tag: 'td',
24533                     cls: 'separator'
24534                 },
24535                 {
24536                     tag: 'td',
24537                     cn: [
24538                         {
24539                             tag: 'button',
24540                             type: 'button',
24541                             cls: 'btn btn-primary period',
24542                             html: 'AM'
24543                             
24544                         }
24545                     ]
24546                 }
24547             ]
24548         });
24549         
24550         time.createChild({
24551             tag: 'tr',
24552             cn: [
24553                 {
24554                     tag: 'td',
24555                     cn: [
24556                         {
24557                             tag: 'a',
24558                             href: '#',
24559                             cls: 'btn',
24560                             cn: [
24561                                 {
24562                                     tag: 'span',
24563                                     cls: 'hours-down fa fas fa-chevron-down'
24564                                 }
24565                             ]
24566                         }
24567                     ]
24568                 },
24569                 {
24570                     tag: 'td',
24571                     cls: 'separator'
24572                 },
24573                 {
24574                     tag: 'td',
24575                     cn: [
24576                         {
24577                             tag: 'a',
24578                             href: '#',
24579                             cls: 'btn',
24580                             cn: [
24581                                 {
24582                                     tag: 'span',
24583                                     cls: 'minutes-down fa fas fa-chevron-down'
24584                                 }
24585                             ]
24586                         }
24587                     ]
24588                 },
24589                 {
24590                     tag: 'td',
24591                     cls: 'separator'
24592                 }
24593             ]
24594         });
24595         
24596     },
24597     
24598     update: function()
24599     {
24600         // default minute is a multiple of minuteStep
24601         if(typeof(this.time) === 'undefined' || this.time.length == 0) {
24602             this.time = new Date();
24603             this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24604         }
24605         this.time = (typeof(this.time) === 'undefined' || this.time.length == 0) ? new Date() : this.time;
24606         
24607         this.fill();
24608     },
24609     
24610     fill: function() 
24611     {
24612         var hours = this.time.getHours();
24613         var minutes = this.time.getMinutes();
24614         var period = Roo.bootstrap.form.TimeField.periodText[this.language]['am'];
24615         
24616         if(hours > 11){
24617             period = Roo.bootstrap.form.TimeField.periodText[this.language]['pm'];
24618         }
24619         
24620         if(hours == 0){
24621             hours = 12;
24622         }
24623         
24624         
24625         if(hours > 12){
24626             hours = hours - 12;
24627         }
24628         
24629         if(hours < 10){
24630             hours = '0' + hours;
24631         }
24632         
24633         if(minutes < 10){
24634             minutes = '0' + minutes;
24635         }
24636         
24637         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24638         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24639         this.pop.select('button', true).first().dom.innerHTML = period;
24640         
24641     },
24642     
24643     place: function()
24644     {   
24645         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24646         
24647         var cls = ['bottom'];
24648         
24649         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24650             cls.pop();
24651             cls.push('top');
24652         }
24653         
24654         cls.push('right');
24655         
24656         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24657             cls.pop();
24658             cls.push('left');
24659         }
24660         //this.picker().setXY(20000,20000);
24661         this.picker().addClass(cls.join('-'));
24662         
24663         var _this = this;
24664         
24665         Roo.each(cls, function(c){
24666             if(c == 'bottom'){
24667                 (function() {
24668                  //  
24669                 }).defer(200);
24670                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24671                 //_this.picker().setTop(_this.inputEl().getHeight());
24672                 return;
24673             }
24674             if(c == 'top'){
24675                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24676                 
24677                 //_this.picker().setTop(0 - _this.picker().getHeight());
24678                 return;
24679             }
24680             /*
24681             if(c == 'left'){
24682                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24683                 return;
24684             }
24685             if(c == 'right'){
24686                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24687                 return;
24688             }
24689             */
24690         });
24691         
24692     },
24693   
24694     onFocus : function()
24695     {
24696         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24697         this.show();
24698     },
24699     
24700     onBlur : function()
24701     {
24702         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24703         this.hide();
24704     },
24705     
24706     show : function()
24707     {
24708         this.picker().show();
24709         this.pop.show();
24710         this.update();
24711         this.place();
24712         
24713         this.fireEvent('show', this, this.time);
24714     },
24715     
24716     hide : function()
24717     {
24718         this.picker().hide();
24719         this.pop.hide();
24720         
24721         this.fireEvent('hide', this, this.time);
24722     },
24723     
24724     setTime : function()
24725     {
24726         this.hide();
24727         this.setValue(this.time);
24728         
24729         this.fireEvent('select', this, this.time);
24730         
24731         
24732     },
24733
24734     // return false when it fails
24735     parseTime : function(value)
24736     {
24737         if(!value) {
24738             return false;
24739         }
24740         if(value instanceof Date){
24741             return value;
24742         }
24743         var v = Date.parseDate(value, 'H:i:s');
24744
24745         return (typeof(v) == 'undefined') ? false : v;
24746     },
24747
24748     translateTime : function(time)
24749     {
24750         switch(this.language) {
24751             case 'zh_CN':
24752                 return new Intl.DateTimeFormat('zh-CN', {
24753                     hour : 'numeric',
24754                     minute : 'numeric',
24755                     hour12 : true
24756                 }).format(time);
24757             default :
24758                 return time.format(this.format);
24759         }
24760     },
24761
24762     setValue: function(v)
24763     {
24764         var t = this.parseTime(v);
24765
24766         if(!t) {
24767             this.time = this.value = this.hiddenField.value =  '';
24768             if(this.rendered){
24769                 this.inputEl().dom.value = '';
24770                 this.validate();
24771             }
24772             return;
24773         }
24774
24775         this.value = this.hiddenField.value = t.dateFormat('H:i:s');
24776
24777         v = this.translateTime(t);
24778
24779         if(this.rendered){
24780             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
24781             this.validate();
24782         }
24783
24784         this.time = t;
24785
24786         this.update();
24787     },
24788
24789     setRawValue: function(v)
24790     {
24791         var t = this.parseTime(v);
24792
24793         if(!t) {
24794             this.time = this.value = this.hiddenField.value =  '';
24795             if(this.rendered){
24796                 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
24797             }
24798             return;
24799         }
24800
24801         this.value = this.hiddenField.value = t.dateFormat('H:i:s');
24802
24803         v = this.translateTime(t);
24804
24805         if(this.rendered){
24806             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
24807         }
24808
24809         this.time = t;
24810
24811         this.update();
24812     },
24813
24814     getValue: function()
24815     {
24816         return this.value;
24817     },
24818
24819     getRawValue : function(){
24820         return this.getValue();
24821     },
24822     
24823     onMousedown: function(e){
24824         e.stopPropagation();
24825         e.preventDefault();
24826     },
24827     
24828     onIncrementHours: function()
24829     {
24830         Roo.log('onIncrementHours');
24831         this.time = this.time.add(Date.HOUR, 1);
24832         this.update();
24833         
24834     },
24835     
24836     onDecrementHours: function()
24837     {
24838         Roo.log('onDecrementHours');
24839         this.time = this.time.add(Date.HOUR, -1);
24840         this.update();
24841     },
24842     
24843     onIncrementMinutes: function()
24844     {
24845         Roo.log('onIncrementMinutes');
24846         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24847         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24848         this.update();
24849     },
24850     
24851     onDecrementMinutes: function()
24852     {
24853         Roo.log('onDecrementMinutes');
24854         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24855         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24856         this.update();
24857     },
24858     
24859     onTogglePeriod: function()
24860     {
24861         Roo.log('onTogglePeriod');
24862         this.time = this.time.add(Date.HOUR, 12);
24863         this.update();
24864     }
24865     
24866    
24867 });
24868 Roo.apply(Roo.bootstrap.form.TimeField,  {
24869     okText : 'OK',
24870     periodText : {
24871         en : {
24872             am : 'AM',
24873             pm : 'PM'
24874         },
24875         zh_CN : {
24876             am : '上午',
24877             pm : '下午'
24878         }
24879     }
24880 });
24881
24882 Roo.apply(Roo.bootstrap.form.TimeField,  {
24883     template : {
24884         tag: 'div',
24885         cls: 'datepicker dropdown-menu',
24886         cn: [
24887             {
24888                 tag: 'div',
24889                 cls: 'datepicker-time',
24890                 cn: [
24891                 {
24892                     tag: 'table',
24893                     cls: 'table-condensed',
24894                     cn:[
24895                         {
24896                             tag: 'tbody',
24897                             cn: [
24898                                 {
24899                                     tag: 'tr',
24900                                     cn: [
24901                                     {
24902                                         tag: 'td',
24903                                         colspan: '7'
24904                                     }
24905                                     ]
24906                                 }
24907                             ]
24908                         },
24909                         {
24910                             tag: 'tfoot',
24911                             cn: [
24912                                 {
24913                                     tag: 'tr',
24914                                     cn: [
24915                                     {
24916                                         tag: 'th',
24917                                         colspan: '7',
24918                                         cls: '',
24919                                         cn: [
24920                                             {
24921                                                 tag: 'button',
24922                                                 cls: 'btn btn-info ok',
24923                                                 html: "OK" // this is overridden on construciton
24924                                             }
24925                                         ]
24926                                     }
24927                     
24928                                     ]
24929                                 }
24930                             ]
24931                         }
24932                     ]
24933                 }
24934                 ]
24935             }
24936         ]
24937     }
24938 });
24939
24940  
24941
24942  /*
24943  * - LGPL
24944  *
24945  * MonthField
24946  * 
24947  */
24948
24949 /**
24950  * @class Roo.bootstrap.form.MonthField
24951  * @extends Roo.bootstrap.form.Input
24952  * Bootstrap MonthField class
24953  * 
24954  * @cfg {String} language default en
24955  * 
24956  * @constructor
24957  * Create a new MonthField
24958  * @param {Object} config The config object
24959  */
24960
24961 Roo.bootstrap.form.MonthField = function(config){
24962     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24963     
24964     this.addEvents({
24965         /**
24966          * @event show
24967          * Fires when this field show.
24968          * @param {Roo.bootstrap.form.MonthField} this
24969          * @param {Mixed} date The date value
24970          */
24971         show : true,
24972         /**
24973          * @event show
24974          * Fires when this field hide.
24975          * @param {Roo.bootstrap.form.MonthField} this
24976          * @param {Mixed} date The date value
24977          */
24978         hide : true,
24979         /**
24980          * @event select
24981          * Fires when select a date.
24982          * @param {Roo.bootstrap.form.MonthField} this
24983          * @param {String} oldvalue The old value
24984          * @param {String} newvalue The new value
24985          */
24986         select : true
24987     });
24988 };
24989
24990 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24991     
24992     onRender: function(ct, position)
24993     {
24994         
24995         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24996         
24997         this.language = this.language || 'en';
24998         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24999         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
25000         
25001         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
25002         this.isInline = false;
25003         this.isInput = true;
25004         this.component = this.el.select('.add-on', true).first() || false;
25005         this.component = (this.component && this.component.length === 0) ? false : this.component;
25006         this.hasInput = this.component && this.inputEL().length;
25007         
25008         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
25009         
25010         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25011         
25012         this.picker().on('mousedown', this.onMousedown, this);
25013         this.picker().on('click', this.onClick, this);
25014         
25015         this.picker().addClass('datepicker-dropdown');
25016         
25017         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
25018             v.setStyle('width', '189px');
25019         });
25020         
25021         this.fillMonths();
25022         
25023         this.update();
25024         
25025         if(this.isInline) {
25026             this.show();
25027         }
25028         
25029     },
25030     
25031     setValue: function(v, suppressEvent)
25032     {   
25033         var o = this.getValue();
25034         
25035         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
25036         
25037         this.update();
25038
25039         if(suppressEvent !== true){
25040             this.fireEvent('select', this, o, v);
25041         }
25042         
25043     },
25044     
25045     getValue: function()
25046     {
25047         return this.value;
25048     },
25049     
25050     onClick: function(e) 
25051     {
25052         e.stopPropagation();
25053         e.preventDefault();
25054         
25055         var target = e.getTarget();
25056         
25057         if(target.nodeName.toLowerCase() === 'i'){
25058             target = Roo.get(target).dom.parentNode;
25059         }
25060         
25061         var nodeName = target.nodeName;
25062         var className = target.className;
25063         var html = target.innerHTML;
25064         
25065         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
25066             return;
25067         }
25068         
25069         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
25070         
25071         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
25072         
25073         this.hide();
25074                         
25075     },
25076     
25077     picker : function()
25078     {
25079         return this.pickerEl;
25080     },
25081     
25082     fillMonths: function()
25083     {    
25084         var i = 0;
25085         var months = this.picker().select('>.datepicker-months td', true).first();
25086         
25087         months.dom.innerHTML = '';
25088         
25089         while (i < 12) {
25090             var month = {
25091                 tag: 'span',
25092                 cls: 'month',
25093                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
25094             };
25095             
25096             months.createChild(month);
25097         }
25098         
25099     },
25100     
25101     update: function()
25102     {
25103         var _this = this;
25104         
25105         if(typeof(this.vIndex) == 'undefined' && this.value.length){
25106             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
25107         }
25108         
25109         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
25110             e.removeClass('active');
25111             
25112             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
25113                 e.addClass('active');
25114             }
25115         })
25116     },
25117     
25118     place: function()
25119     {
25120         if(this.isInline) {
25121             return;
25122         }
25123         
25124         this.picker().removeClass(['bottom', 'top']);
25125         
25126         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
25127             /*
25128              * place to the top of element!
25129              *
25130              */
25131             
25132             this.picker().addClass('top');
25133             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
25134             
25135             return;
25136         }
25137         
25138         this.picker().addClass('bottom');
25139         
25140         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
25141     },
25142     
25143     onFocus : function()
25144     {
25145         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
25146         this.show();
25147     },
25148     
25149     onBlur : function()
25150     {
25151         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
25152         
25153         var d = this.inputEl().getValue();
25154         
25155         this.setValue(d);
25156                 
25157         this.hide();
25158     },
25159     
25160     show : function()
25161     {
25162         this.picker().show();
25163         this.picker().select('>.datepicker-months', true).first().show();
25164         this.update();
25165         this.place();
25166         
25167         this.fireEvent('show', this, this.date);
25168     },
25169     
25170     hide : function()
25171     {
25172         if(this.isInline) {
25173             return;
25174         }
25175         this.picker().hide();
25176         this.fireEvent('hide', this, this.date);
25177         
25178     },
25179     
25180     onMousedown: function(e)
25181     {
25182         e.stopPropagation();
25183         e.preventDefault();
25184     },
25185     
25186     keyup: function(e)
25187     {
25188         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
25189         this.update();
25190     },
25191
25192     fireKey: function(e)
25193     {
25194         if (!this.picker().isVisible()){
25195             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
25196                 this.show();
25197             }
25198             return;
25199         }
25200         
25201         var dir;
25202         
25203         switch(e.keyCode){
25204             case 27: // escape
25205                 this.hide();
25206                 e.preventDefault();
25207                 break;
25208             case 37: // left
25209             case 39: // right
25210                 dir = e.keyCode == 37 ? -1 : 1;
25211                 
25212                 this.vIndex = this.vIndex + dir;
25213                 
25214                 if(this.vIndex < 0){
25215                     this.vIndex = 0;
25216                 }
25217                 
25218                 if(this.vIndex > 11){
25219                     this.vIndex = 11;
25220                 }
25221                 
25222                 if(isNaN(this.vIndex)){
25223                     this.vIndex = 0;
25224                 }
25225                 
25226                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
25227                 
25228                 break;
25229             case 38: // up
25230             case 40: // down
25231                 
25232                 dir = e.keyCode == 38 ? -1 : 1;
25233                 
25234                 this.vIndex = this.vIndex + dir * 4;
25235                 
25236                 if(this.vIndex < 0){
25237                     this.vIndex = 0;
25238                 }
25239                 
25240                 if(this.vIndex > 11){
25241                     this.vIndex = 11;
25242                 }
25243                 
25244                 if(isNaN(this.vIndex)){
25245                     this.vIndex = 0;
25246                 }
25247                 
25248                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
25249                 break;
25250                 
25251             case 13: // enter
25252                 
25253                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
25254                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
25255                 }
25256                 
25257                 this.hide();
25258                 e.preventDefault();
25259                 break;
25260             case 9: // tab
25261                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
25262                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
25263                 }
25264                 this.hide();
25265                 break;
25266             case 16: // shift
25267             case 17: // ctrl
25268             case 18: // alt
25269                 break;
25270             default :
25271                 this.hide();
25272                 
25273         }
25274     },
25275     
25276     remove: function() 
25277     {
25278         this.picker().remove();
25279     }
25280    
25281 });
25282
25283 Roo.apply(Roo.bootstrap.form.MonthField,  {
25284     
25285     content : {
25286         tag: 'tbody',
25287         cn: [
25288         {
25289             tag: 'tr',
25290             cn: [
25291             {
25292                 tag: 'td',
25293                 colspan: '7'
25294             }
25295             ]
25296         }
25297         ]
25298     },
25299     
25300     dates:{
25301         en: {
25302             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
25303             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
25304         }
25305     }
25306 });
25307
25308 Roo.apply(Roo.bootstrap.form.MonthField,  {
25309   
25310     template : {
25311         tag: 'div',
25312         cls: 'datepicker dropdown-menu roo-dynamic',
25313         cn: [
25314             {
25315                 tag: 'div',
25316                 cls: 'datepicker-months',
25317                 cn: [
25318                 {
25319                     tag: 'table',
25320                     cls: 'table-condensed',
25321                     cn:[
25322                         Roo.bootstrap.form.DateField.content
25323                     ]
25324                 }
25325                 ]
25326             }
25327         ]
25328     }
25329 });
25330
25331  
25332
25333  
25334  /*
25335  * - LGPL
25336  *
25337  * CheckBox
25338  * 
25339  */
25340
25341 /**
25342  * @class Roo.bootstrap.form.CheckBox
25343  * @extends Roo.bootstrap.form.Input
25344  * Bootstrap CheckBox class
25345  * 
25346  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
25347  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
25348  * @cfg {String} boxLabel The text that appears beside the checkbox
25349  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
25350  * @cfg {Boolean} checked initnal the element
25351  * @cfg {Boolean} inline inline the element (default false)
25352  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
25353  * @cfg {String} tooltip label tooltip
25354  * 
25355  * @constructor
25356  * Create a new CheckBox
25357  * @param {Object} config The config object
25358  */
25359
25360 Roo.bootstrap.form.CheckBox = function(config){
25361     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
25362    
25363     this.addEvents({
25364         /**
25365         * @event check
25366         * Fires when the element is checked or unchecked.
25367         * @param {Roo.bootstrap.form.CheckBox} this This input
25368         * @param {Boolean} checked The new checked value
25369         */
25370        check : true,
25371        /**
25372         * @event click
25373         * Fires when the element is click.
25374         * @param {Roo.bootstrap.form.CheckBox} this This input
25375         */
25376        click : true
25377     });
25378     
25379 };
25380
25381 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
25382   
25383     inputType: 'checkbox',
25384     inputValue: 1,
25385     valueOff: 0,
25386     boxLabel: false,
25387     checked: false,
25388     weight : false,
25389     inline: false,
25390     tooltip : '',
25391     
25392     // checkbox success does not make any sense really.. 
25393     invalidClass : "",
25394     validClass : "",
25395     
25396     
25397     getAutoCreate : function()
25398     {
25399         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
25400         
25401         var id = Roo.id();
25402         
25403         var cfg = {};
25404         
25405         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
25406         
25407         if(this.inline){
25408             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
25409         }
25410         
25411         var input =  {
25412             tag: 'input',
25413             id : id,
25414             type : this.inputType,
25415             value : this.inputValue,
25416             cls : 'roo-' + this.inputType, //'form-box',
25417             placeholder : this.placeholder || ''
25418             
25419         };
25420         
25421         if(this.inputType != 'radio'){
25422             var hidden =  {
25423                 tag: 'input',
25424                 type : 'hidden',
25425                 cls : 'roo-hidden-value',
25426                 value : this.checked ? this.inputValue : this.valueOff
25427             };
25428         }
25429         
25430             
25431         if (this.weight) { // Validity check?
25432             cfg.cls += " " + this.inputType + "-" + this.weight;
25433         }
25434         
25435         if (this.disabled) {
25436             input.disabled=true;
25437         }
25438         
25439         if(this.checked){
25440             input.checked = this.checked;
25441         }
25442         
25443         if (this.name) {
25444             
25445             input.name = this.name;
25446             
25447             if(this.inputType != 'radio'){
25448                 hidden.name = this.name;
25449                 input.name = '_hidden_' + this.name;
25450             }
25451         }
25452         
25453         if (this.size) {
25454             input.cls += ' input-' + this.size;
25455         }
25456         
25457         var settings=this;
25458         
25459         ['xs','sm','md','lg'].map(function(size){
25460             if (settings[size]) {
25461                 cfg.cls += ' col-' + size + '-' + settings[size];
25462             }
25463         });
25464         
25465         var inputblock = input;
25466          
25467         if (this.before || this.after) {
25468             
25469             inputblock = {
25470                 cls : 'input-group',
25471                 cn :  [] 
25472             };
25473             
25474             if (this.before) {
25475                 inputblock.cn.push({
25476                     tag :'span',
25477                     cls : 'input-group-addon',
25478                     html : this.before
25479                 });
25480             }
25481             
25482             inputblock.cn.push(input);
25483             
25484             if(this.inputType != 'radio'){
25485                 inputblock.cn.push(hidden);
25486             }
25487             
25488             if (this.after) {
25489                 inputblock.cn.push({
25490                     tag :'span',
25491                     cls : 'input-group-addon',
25492                     html : this.after
25493                 });
25494             }
25495             
25496         }
25497         var boxLabelCfg = false;
25498         
25499         if(this.boxLabel){
25500            
25501             boxLabelCfg = {
25502                 tag: 'label',
25503                 //'for': id, // box label is handled by onclick - so no for...
25504                 cls: 'box-label',
25505                 html: this.boxLabel
25506             };
25507             if(this.tooltip){
25508                 boxLabelCfg.tooltip = this.tooltip;
25509             }
25510              
25511         }
25512         
25513         
25514         if (align ==='left' && this.fieldLabel.length) {
25515 //                Roo.log("left and has label");
25516             cfg.cn = [
25517                 {
25518                     tag: 'label',
25519                     'for' :  id,
25520                     cls : 'control-label',
25521                     html : this.fieldLabel
25522                 },
25523                 {
25524                     cls : "", 
25525                     cn: [
25526                         inputblock
25527                     ]
25528                 }
25529             ];
25530             
25531             if (boxLabelCfg) {
25532                 cfg.cn[1].cn.push(boxLabelCfg);
25533             }
25534             
25535             if(this.labelWidth > 12){
25536                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25537             }
25538             
25539             if(this.labelWidth < 13 && this.labelmd == 0){
25540                 this.labelmd = this.labelWidth;
25541             }
25542             
25543             if(this.labellg > 0){
25544                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25545                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25546             }
25547             
25548             if(this.labelmd > 0){
25549                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25550                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25551             }
25552             
25553             if(this.labelsm > 0){
25554                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25555                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25556             }
25557             
25558             if(this.labelxs > 0){
25559                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25560                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25561             }
25562             
25563         } else if ( this.fieldLabel.length) {
25564 //                Roo.log(" label");
25565                 cfg.cn = [
25566                    
25567                     {
25568                         tag: this.boxLabel ? 'span' : 'label',
25569                         'for': id,
25570                         cls: 'control-label box-input-label',
25571                         //cls : 'input-group-addon',
25572                         html : this.fieldLabel
25573                     },
25574                     
25575                     inputblock
25576                     
25577                 ];
25578                 if (boxLabelCfg) {
25579                     cfg.cn.push(boxLabelCfg);
25580                 }
25581
25582         } else {
25583             
25584 //                Roo.log(" no label && no align");
25585                 cfg.cn = [  inputblock ] ;
25586                 if (boxLabelCfg) {
25587                     cfg.cn.push(boxLabelCfg);
25588                 }
25589
25590                 
25591         }
25592         
25593        
25594         
25595         if(this.inputType != 'radio'){
25596             cfg.cn.push(hidden);
25597         }
25598         
25599         return cfg;
25600         
25601     },
25602     
25603     /**
25604      * return the real input element.
25605      */
25606     inputEl: function ()
25607     {
25608         return this.el.select('input.roo-' + this.inputType,true).first();
25609     },
25610     hiddenEl: function ()
25611     {
25612         return this.el.select('input.roo-hidden-value',true).first();
25613     },
25614     
25615     labelEl: function()
25616     {
25617         return this.el.select('label.control-label',true).first();
25618     },
25619     /* depricated... */
25620     
25621     label: function()
25622     {
25623         return this.labelEl();
25624     },
25625     
25626     boxLabelEl: function()
25627     {
25628         return this.el.select('label.box-label',true).first();
25629     },
25630     
25631     initEvents : function()
25632     {
25633 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25634         
25635         this.inputEl().on('click', this.onClick,  this);
25636         
25637         if (this.boxLabel) { 
25638             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25639         }
25640         
25641         this.startValue = this.getValue();
25642         
25643         if(this.groupId){
25644             Roo.bootstrap.form.CheckBox.register(this);
25645         }
25646     },
25647     
25648     onClick : function(e)
25649     {   
25650         if(this.fireEvent('click', this, e) !== false){
25651             this.setChecked(!this.checked);
25652         }
25653         
25654     },
25655     
25656     setChecked : function(state,suppressEvent)
25657     {
25658         this.startValue = this.getValue();
25659
25660         if(this.inputType == 'radio'){
25661             
25662             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25663                 e.dom.checked = false;
25664             });
25665             
25666             this.inputEl().dom.checked = true;
25667             
25668             this.inputEl().dom.value = this.inputValue;
25669             
25670             if(suppressEvent !== true){
25671                 this.fireEvent('check', this, true);
25672             }
25673             
25674             this.validate();
25675             
25676             return;
25677         }
25678         
25679         this.checked = state;
25680         
25681         this.inputEl().dom.checked = state;
25682         
25683         
25684         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25685         
25686         if(suppressEvent !== true){
25687             this.fireEvent('check', this, state);
25688         }
25689         
25690         this.validate();
25691     },
25692     
25693     getValue : function()
25694     {
25695         if(this.inputType == 'radio'){
25696             return this.getGroupValue();
25697         }
25698         
25699         return this.hiddenEl().dom.value;
25700         
25701     },
25702     
25703     getGroupValue : function()
25704     {
25705         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25706             return '';
25707         }
25708         
25709         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25710     },
25711     
25712     setValue : function(v,suppressEvent)
25713     {
25714         if(this.inputType == 'radio'){
25715             this.setGroupValue(v, suppressEvent);
25716             return;
25717         }
25718         
25719         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25720         
25721         this.validate();
25722     },
25723     
25724     setGroupValue : function(v, suppressEvent)
25725     {
25726         this.startValue = this.getValue();
25727         
25728         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25729             e.dom.checked = false;
25730             
25731             if(e.dom.value == v){
25732                 e.dom.checked = true;
25733             }
25734         });
25735         
25736         if(suppressEvent !== true){
25737             this.fireEvent('check', this, true);
25738         }
25739
25740         this.validate();
25741         
25742         return;
25743     },
25744     
25745     validate : function()
25746     {
25747         if(this.getVisibilityEl().hasClass('hidden')){
25748             return true;
25749         }
25750         
25751         if(
25752                 this.disabled || 
25753                 (this.inputType == 'radio' && this.validateRadio()) ||
25754                 (this.inputType == 'checkbox' && this.validateCheckbox())
25755         ){
25756             this.markValid();
25757             return true;
25758         }
25759         
25760         this.markInvalid();
25761         return false;
25762     },
25763     
25764     validateRadio : function()
25765     {
25766         if(this.getVisibilityEl().hasClass('hidden')){
25767             return true;
25768         }
25769         
25770         if(this.allowBlank){
25771             return true;
25772         }
25773         
25774         var valid = false;
25775         
25776         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25777             if(!e.dom.checked){
25778                 return;
25779             }
25780             
25781             valid = true;
25782             
25783             return false;
25784         });
25785         
25786         return valid;
25787     },
25788     
25789     validateCheckbox : function()
25790     {
25791         if(!this.groupId){
25792             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25793             //return (this.getValue() == this.inputValue) ? true : false;
25794         }
25795         
25796         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25797         
25798         if(!group){
25799             return false;
25800         }
25801         
25802         var r = false;
25803         
25804         for(var i in group){
25805             if(group[i].el.isVisible(true)){
25806                 r = false;
25807                 break;
25808             }
25809             
25810             r = true;
25811         }
25812         
25813         for(var i in group){
25814             if(r){
25815                 break;
25816             }
25817             
25818             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25819         }
25820         
25821         return r;
25822     },
25823     
25824     /**
25825      * Mark this field as valid
25826      */
25827     markValid : function()
25828     {
25829         var _this = this;
25830         
25831         this.fireEvent('valid', this);
25832         
25833         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25834         
25835         if(this.groupId){
25836             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25837         }
25838         
25839         if(label){
25840             label.markValid();
25841         }
25842
25843         if(this.inputType == 'radio'){
25844             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25845                 var fg = e.findParent('.form-group', false, true);
25846                 if (Roo.bootstrap.version == 3) {
25847                     fg.removeClass([_this.invalidClass, _this.validClass]);
25848                     fg.addClass(_this.validClass);
25849                 } else {
25850                     fg.removeClass(['is-valid', 'is-invalid']);
25851                     fg.addClass('is-valid');
25852                 }
25853             });
25854             
25855             return;
25856         }
25857
25858         if(!this.groupId){
25859             var fg = this.el.findParent('.form-group', false, true);
25860             if (Roo.bootstrap.version == 3) {
25861                 fg.removeClass([this.invalidClass, this.validClass]);
25862                 fg.addClass(this.validClass);
25863             } else {
25864                 fg.removeClass(['is-valid', 'is-invalid']);
25865                 fg.addClass('is-valid');
25866             }
25867             return;
25868         }
25869         
25870         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25871         
25872         if(!group){
25873             return;
25874         }
25875         
25876         for(var i in group){
25877             var fg = group[i].el.findParent('.form-group', false, true);
25878             if (Roo.bootstrap.version == 3) {
25879                 fg.removeClass([this.invalidClass, this.validClass]);
25880                 fg.addClass(this.validClass);
25881             } else {
25882                 fg.removeClass(['is-valid', 'is-invalid']);
25883                 fg.addClass('is-valid');
25884             }
25885         }
25886     },
25887     
25888      /**
25889      * Mark this field as invalid
25890      * @param {String} msg The validation message
25891      */
25892     markInvalid : function(msg)
25893     {
25894         if(this.allowBlank){
25895             return;
25896         }
25897         
25898         var _this = this;
25899         
25900         this.fireEvent('invalid', this, msg);
25901         
25902         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25903         
25904         if(this.groupId){
25905             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25906         }
25907         
25908         if(label){
25909             label.markInvalid();
25910         }
25911             
25912         if(this.inputType == 'radio'){
25913             
25914             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25915                 var fg = e.findParent('.form-group', false, true);
25916                 if (Roo.bootstrap.version == 3) {
25917                     fg.removeClass([_this.invalidClass, _this.validClass]);
25918                     fg.addClass(_this.invalidClass);
25919                 } else {
25920                     fg.removeClass(['is-invalid', 'is-valid']);
25921                     fg.addClass('is-invalid');
25922                 }
25923             });
25924             
25925             return;
25926         }
25927         
25928         if(!this.groupId){
25929             var fg = this.el.findParent('.form-group', false, true);
25930             if (Roo.bootstrap.version == 3) {
25931                 fg.removeClass([_this.invalidClass, _this.validClass]);
25932                 fg.addClass(_this.invalidClass);
25933             } else {
25934                 fg.removeClass(['is-invalid', 'is-valid']);
25935                 fg.addClass('is-invalid');
25936             }
25937             return;
25938         }
25939         
25940         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25941         
25942         if(!group){
25943             return;
25944         }
25945         
25946         for(var i in group){
25947             var fg = group[i].el.findParent('.form-group', false, true);
25948             if (Roo.bootstrap.version == 3) {
25949                 fg.removeClass([_this.invalidClass, _this.validClass]);
25950                 fg.addClass(_this.invalidClass);
25951             } else {
25952                 fg.removeClass(['is-invalid', 'is-valid']);
25953                 fg.addClass('is-invalid');
25954             }
25955         }
25956         
25957     },
25958     
25959     clearInvalid : function()
25960     {
25961         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25962         
25963         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25964         
25965         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25966         
25967         if (label && label.iconEl) {
25968             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25969             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25970         }
25971     },
25972     
25973     disable : function()
25974     {
25975         if(this.inputType != 'radio'){
25976             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25977             return;
25978         }
25979         
25980         var _this = this;
25981         
25982         if(this.rendered){
25983             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25984                 _this.getActionEl().addClass(this.disabledClass);
25985                 e.dom.disabled = true;
25986             });
25987         }
25988         
25989         this.disabled = true;
25990         this.fireEvent("disable", this);
25991         return this;
25992     },
25993
25994     enable : function()
25995     {
25996         if(this.inputType != 'radio'){
25997             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25998             return;
25999         }
26000         
26001         var _this = this;
26002         
26003         if(this.rendered){
26004             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
26005                 _this.getActionEl().removeClass(this.disabledClass);
26006                 e.dom.disabled = false;
26007             });
26008         }
26009         
26010         this.disabled = false;
26011         this.fireEvent("enable", this);
26012         return this;
26013     },
26014     
26015     setBoxLabel : function(v)
26016     {
26017         this.boxLabel = v;
26018         
26019         if(this.rendered){
26020             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
26021         }
26022     }
26023
26024 });
26025
26026 Roo.apply(Roo.bootstrap.form.CheckBox, {
26027     
26028     groups: {},
26029     
26030      /**
26031     * register a CheckBox Group
26032     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
26033     */
26034     register : function(checkbox)
26035     {
26036         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
26037             this.groups[checkbox.groupId] = {};
26038         }
26039         
26040         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
26041             return;
26042         }
26043         
26044         this.groups[checkbox.groupId][checkbox.name] = checkbox;
26045         
26046     },
26047     /**
26048     * fetch a CheckBox Group based on the group ID
26049     * @param {string} the group ID
26050     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
26051     */
26052     get: function(groupId) {
26053         if (typeof(this.groups[groupId]) == 'undefined') {
26054             return false;
26055         }
26056         
26057         return this.groups[groupId] ;
26058     }
26059     
26060     
26061 });
26062 /*
26063  * - LGPL
26064  *
26065  * RadioItem
26066  * 
26067  */
26068
26069 /**
26070  * @class Roo.bootstrap.form.Radio
26071  * @extends Roo.bootstrap.Component
26072  * Bootstrap Radio class
26073  * @cfg {String} boxLabel - the label associated
26074  * @cfg {String} value - the value of radio
26075  * 
26076  * @constructor
26077  * Create a new Radio
26078  * @param {Object} config The config object
26079  */
26080 Roo.bootstrap.form.Radio = function(config){
26081     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
26082     
26083 };
26084
26085 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
26086     
26087     boxLabel : '',
26088     
26089     value : '',
26090     
26091     getAutoCreate : function()
26092     {
26093         var cfg = {
26094             tag : 'div',
26095             cls : 'form-group radio',
26096             cn : [
26097                 {
26098                     tag : 'label',
26099                     cls : 'box-label',
26100                     html : this.boxLabel
26101                 }
26102             ]
26103         };
26104         
26105         return cfg;
26106     },
26107     
26108     initEvents : function() 
26109     {
26110         this.parent().register(this);
26111         
26112         this.el.on('click', this.onClick, this);
26113         
26114     },
26115     
26116     onClick : function(e)
26117     {
26118         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
26119             this.setChecked(true);
26120         }
26121     },
26122     
26123     setChecked : function(state, suppressEvent)
26124     {
26125         this.parent().setValue(this.value, suppressEvent);
26126         
26127     },
26128     
26129     setBoxLabel : function(v)
26130     {
26131         this.boxLabel = v;
26132         
26133         if(this.rendered){
26134             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
26135         }
26136     }
26137     
26138 });
26139  
26140
26141  /*
26142  * - LGPL
26143  *
26144  * Input
26145  * 
26146  */
26147
26148 /**
26149  * @class Roo.bootstrap.form.SecurePass
26150  * @extends Roo.bootstrap.form.Input
26151  * Bootstrap SecurePass class
26152  * @cfg {Number} minimumStrength invalid if the strength of the password input is less than the minimum strength (from 0 to 3) (default 2)
26153  *
26154  * 
26155  * @constructor
26156  * Create a new SecurePass
26157  * @param {Object} config The config object
26158  */
26159  
26160 Roo.bootstrap.form.SecurePass = function (config) {
26161     
26162     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
26163 }
26164
26165 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
26166     minimumStrength : 2,
26167     // private
26168     meterWidth: 300, 
26169     imageRoot: '/',  
26170     // private
26171     strength: 0,
26172     // private
26173     _lastPwd: null,
26174     // private
26175     kCapitalLetter: 0,
26176     kSmallLetter: 1,
26177     kDigit: 2,
26178     kPunctuation: 3,
26179     
26180     insecure: false,
26181     // private
26182     initEvents: function ()
26183     {
26184         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
26185
26186         if (this.el.is('input[type=password]') && Roo.isSafari) {
26187             this.el.on('keydown', this.SafariOnKeyDown, this);
26188         }
26189
26190         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
26191     },
26192     // private
26193     onRender: function (ct, position)
26194     {
26195         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
26196         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
26197         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
26198
26199         this.trigger.createChild({
26200                    cn: [
26201                     {
26202                     //id: 'PwdMeter',
26203                     tag: 'div',
26204                     cls: 'roo-password-meter-grey col-xs-12',
26205                     style: {
26206                         //width: 0,
26207                         //width: this.meterWidth + 'px'                                                
26208                         }
26209                     },
26210                     {                            
26211                          cls: 'roo-password-meter-text'                          
26212                     }
26213                 ]            
26214         });
26215
26216          
26217         if (this.hideTrigger) {
26218             this.trigger.setDisplayed(false);
26219         }
26220         this.setSize(this.width || '', this.height || '');
26221     },
26222     // private
26223     onDestroy: function ()
26224     {
26225         if (this.trigger) {
26226             this.trigger.removeAllListeners();
26227             this.trigger.remove();
26228         }
26229         if (this.wrap) {
26230             this.wrap.remove();
26231         }
26232         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
26233     },
26234     // private
26235     checkStrength: function ()
26236     {
26237         var pwd = this.inputEl().getValue();
26238         if (pwd == this._lastPwd) {
26239             return;
26240         }
26241
26242         var strength;
26243         if (this.ClientSideStrongPassword(pwd)) {
26244             strength = 3;
26245         } else if (this.ClientSideMediumPassword(pwd)) {
26246             strength = 2;
26247         } else if (this.ClientSideWeakPassword(pwd)) {
26248             strength = 1;
26249         } else {
26250             strength = 0;
26251         }
26252         
26253         Roo.log('strength1: ' + strength);
26254         
26255         //var pm = this.trigger.child('div/div/div').dom;
26256         var pm = this.trigger.child('div/div');
26257         pm.removeClass(Roo.bootstrap.form.SecurePass.meterClass);
26258         pm.addClass(Roo.bootstrap.form.SecurePass.meterClass[strength]);
26259                 
26260         
26261         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
26262                 
26263         pt.innerHTML = Roo.bootstrap.form.SecurePass.meterLabel + '&nbsp;' + Roo.bootstrap.form.SecurePass.pwdStrengths[strength];
26264         
26265         this._lastPwd = pwd;
26266     },
26267     reset: function ()
26268     {
26269         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
26270         
26271         this._lastPwd = '';
26272         
26273         var pm = this.trigger.child('div/div');
26274         pm.removeClass(Roo.bootstrap.form.SecurePass.meterClass);
26275         pm.addClass('roo-password-meter-grey');        
26276         
26277         
26278         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
26279         
26280         pt.innerHTML = '';
26281         this.inputEl().dom.type='password';
26282     },
26283     // private
26284     validateValue: function (value)
26285     {
26286         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
26287             return false;
26288         }
26289         if (value.length == 0) {
26290             if (this.allowBlank) {
26291                 this.clearInvalid();
26292                 return true;
26293             }
26294
26295             this.invalidText = Roo.bootstrap.form.SecurePass.errors.PwdEmpty;
26296             return false;
26297         }
26298         
26299         if(this.insecure){
26300             return true;
26301         }
26302         
26303         if (!value.match(/[\x21-\x7e]+/)) {
26304             this.invalidText = Roo.bootstrap.form.SecurePass.errors.PwdBadChar;
26305             return false;
26306         }
26307         if (value.length < 6) {
26308             this.invalidText = Roo.bootstrap.form.SecurePass.errors.PwdShort;
26309             return false;
26310         }
26311         if (value.length > 16) {
26312             this.invalidText = Roo.bootstrap.form.SecurePass.errors.PwdLong;
26313             return false;
26314         }
26315         var strength;
26316         if (this.ClientSideStrongPassword(value)) {
26317             strength = 3;
26318         } else if (this.ClientSideMediumPassword(value)) {
26319             strength = 2;
26320         } else if (this.ClientSideWeakPassword(value)) {
26321             strength = 1;
26322         } else {
26323             strength = 0;
26324         }
26325
26326         
26327         if (strength < this.minimumStrength) {
26328             this.invalidText = Roo.bootstrap.form.SecurePass.errors.TooWeak;
26329             return false;
26330         }
26331         
26332         
26333         console.log('strength2: ' + strength);
26334         
26335         //var pm = this.trigger.child('div/div/div').dom;
26336         
26337         var pm = this.trigger.child('div/div');
26338         pm.removeClass(Roo.bootstrap.form.SecurePass.meterClass);
26339         pm.addClass(Roo.bootstrap.form.SecurePass.meterClass[strength]);
26340                 
26341         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
26342                 
26343         pt.innerHTML = Roo.bootstrap.form.SecurePass.meterLabel + '&nbsp;' + Roo.bootstrap.form.SecurePass.pwdStrengths[strength];
26344
26345         return true;
26346     },
26347     // private
26348     CharacterSetChecks: function (type)
26349     {
26350         this.type = type;
26351         this.fResult = false;
26352     },
26353     // private
26354     isctype: function (character, type)
26355     {
26356         switch (type) {  
26357             case this.kCapitalLetter:
26358                 if (character >= 'A' && character <= 'Z') {
26359                     return true;
26360                 }
26361                 break;
26362             
26363             case this.kSmallLetter:
26364                 if (character >= 'a' && character <= 'z') {
26365                     return true;
26366                 }
26367                 break;
26368             
26369             case this.kDigit:
26370                 if (character >= '0' && character <= '9') {
26371                     return true;
26372                 }
26373                 break;
26374             
26375             case this.kPunctuation:
26376                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
26377                     return true;
26378                 }
26379                 break;
26380             
26381             default:
26382                 return false;
26383         }
26384
26385     },
26386     // private
26387     IsLongEnough: function (pwd, size)
26388     {
26389         return !(pwd == null || isNaN(size) || pwd.length < size);
26390     },
26391     // private
26392     SpansEnoughCharacterSets: function (word, nb)
26393     {
26394         if (!this.IsLongEnough(word, nb))
26395         {
26396             return false;
26397         }
26398
26399         var characterSetChecks = new Array(
26400             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
26401             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
26402         );
26403         
26404         for (var index = 0; index < word.length; ++index) {
26405             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26406                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26407                     characterSetChecks[nCharSet].fResult = true;
26408                     break;
26409                 }
26410             }
26411         }
26412
26413         var nCharSets = 0;
26414         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26415             if (characterSetChecks[nCharSet].fResult) {
26416                 ++nCharSets;
26417             }
26418         }
26419
26420         if (nCharSets < nb) {
26421             return false;
26422         }
26423         return true;
26424     },
26425     // private
26426     ClientSideStrongPassword: function (pwd)
26427     {
26428         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26429     },
26430     // private
26431     ClientSideMediumPassword: function (pwd)
26432     {
26433         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26434     },
26435     // private
26436     ClientSideWeakPassword: function (pwd)
26437     {
26438         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26439     }
26440           
26441 });
26442
26443 Roo.bootstrap.form.SecurePass.errors = {
26444     PwdEmpty: "Please type a password, and then retype it to confirm.",
26445     PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
26446     PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
26447     PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
26448     IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
26449     FNInPwd: "Your password can't contain your first name. Please type a different password.",
26450     LNInPwd: "Your password can't contain your last name. Please type a different password.",
26451     TooWeak: "Your password is Too Weak."
26452 };
26453
26454 Roo.bootstrap.form.SecurePass.meterLabel = "Password strength:";
26455 Roo.bootstrap.form.SecurePass.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
26456 Roo.bootstrap.form.SecurePass.meterClass = [
26457     "roo-password-meter-tooweak", 
26458     "roo-password-meter-weak", 
26459     "roo-password-meter-medium", 
26460     "roo-password-meter-strong", 
26461     "roo-password-meter-grey"
26462 ];/**
26463  * @class Roo.bootstrap.form.Password
26464  * @extends Roo.bootstrap.form.Input
26465  * Bootstrap Password class
26466  * 
26467  * 
26468  * 
26469  * 
26470  * @constructor
26471  * Create a new Password
26472  * @param {Object} config The config object
26473  */
26474
26475 Roo.bootstrap.form.Password = function(config){
26476     Roo.bootstrap.form.Password.superclass.constructor.call(this, config);
26477
26478     this.inputType = 'password';
26479 };
26480
26481 Roo.extend(Roo.bootstrap.form.Password, Roo.bootstrap.form.Input, {
26482
26483     onRender : function(ct, position)
26484     {
26485         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
26486
26487         this.el.addClass('form-password');
26488
26489         this.wrap = this.inputEl().wrap({
26490             cls : 'password-wrap'
26491         });
26492
26493         this.toggle = this.wrap.createChild({
26494             tag : 'Button',
26495             cls : 'password-toggle'
26496         });
26497
26498
26499         this.toggleEl().addClass('password-hidden');
26500
26501         this.toggleEl().on('click', this.onToggleClick, this);;
26502     },
26503
26504     toggleEl: function()
26505     {
26506         return this.el.select('button.password-toggle',true).first();
26507     },
26508
26509     onToggleClick : function(e) 
26510     {
26511         var input = this.inputEl();
26512         var toggle = this.toggleEl();
26513
26514         toggle.removeClass(['password-visible', 'password-hidden']);
26515
26516         if(input.attr('type') == 'password') {
26517             input.attr('type', 'text');
26518             toggle.addClass('password-visible');
26519         }
26520         else {
26521             input.attr('type', 'password');
26522             toggle.addClass('password-hidden');
26523         }
26524     }
26525 });Roo.rtf = {}; // namespace
26526 Roo.rtf.Hex = function(hex)
26527 {
26528     this.hexstr = hex;
26529 };
26530 Roo.rtf.Paragraph = function(opts)
26531 {
26532     this.content = []; ///??? is that used?
26533 };Roo.rtf.Span = function(opts)
26534 {
26535     this.value = opts.value;
26536 };
26537
26538 Roo.rtf.Group = function(parent)
26539 {
26540     // we dont want to acutally store parent - it will make debug a nightmare..
26541     this.content = [];
26542     this.cn  = [];
26543      
26544        
26545     
26546 };
26547
26548 Roo.rtf.Group.prototype = {
26549     ignorable : false,
26550     content: false,
26551     cn: false,
26552     addContent : function(node) {
26553         // could set styles...
26554         this.content.push(node);
26555     },
26556     addChild : function(cn)
26557     {
26558         this.cn.push(cn);
26559     },
26560     // only for images really...
26561     toDataURL : function()
26562     {
26563         var mimetype = false;
26564         switch(true) {
26565             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26566                 mimetype = "image/png";
26567                 break;
26568              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26569                 mimetype = "image/jpeg";
26570                 break;
26571             default :
26572                 return 'about:blank'; // ?? error?
26573         }
26574         
26575         
26576         var hexstring = this.content[this.content.length-1].value;
26577         
26578         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26579             return String.fromCharCode(parseInt(a, 16));
26580         }).join(""));
26581     }
26582     
26583 };
26584 // this looks like it's normally the {rtf{ .... }}
26585 Roo.rtf.Document = function()
26586 {
26587     // we dont want to acutally store parent - it will make debug a nightmare..
26588     this.rtlch  = [];
26589     this.content = [];
26590     this.cn = [];
26591     
26592 };
26593 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26594     addChild : function(cn)
26595     {
26596         this.cn.push(cn);
26597         switch(cn.type) {
26598             case 'rtlch': // most content seems to be inside this??
26599             case 'listtext':
26600             case 'shpinst':
26601                 this.rtlch.push(cn);
26602                 return;
26603             default:
26604                 this[cn.type] = cn;
26605         }
26606         
26607     },
26608     
26609     getElementsByType : function(type)
26610     {
26611         var ret =  [];
26612         this._getElementsByType(type, ret, this.cn, 'rtf');
26613         return ret;
26614     },
26615     _getElementsByType : function (type, ret, search_array, path)
26616     {
26617         search_array.forEach(function(n,i) {
26618             if (n.type == type) {
26619                 n.path = path + '/' + n.type + ':' + i;
26620                 ret.push(n);
26621             }
26622             if (n.cn.length > 0) {
26623                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26624             }
26625         },this);
26626     }
26627     
26628 });
26629  
26630 Roo.rtf.Ctrl = function(opts)
26631 {
26632     this.value = opts.value;
26633     this.param = opts.param;
26634 };
26635 /**
26636  *
26637  *
26638  * based on this https://github.com/iarna/rtf-parser
26639  * it's really only designed to extract pict from pasted RTF 
26640  *
26641  * usage:
26642  *
26643  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26644  *  
26645  *
26646  */
26647
26648  
26649
26650
26651
26652 Roo.rtf.Parser = function(text) {
26653     //super({objectMode: true})
26654     this.text = '';
26655     this.parserState = this.parseText;
26656     
26657     // these are for interpeter...
26658     this.doc = {};
26659     ///this.parserState = this.parseTop
26660     this.groupStack = [];
26661     this.hexStore = [];
26662     this.doc = false;
26663     
26664     this.groups = []; // where we put the return.
26665     
26666     for (var ii = 0; ii < text.length; ++ii) {
26667         ++this.cpos;
26668         
26669         if (text[ii] === '\n') {
26670             ++this.row;
26671             this.col = 1;
26672         } else {
26673             ++this.col;
26674         }
26675         this.parserState(text[ii]);
26676     }
26677     
26678     
26679     
26680 };
26681 Roo.rtf.Parser.prototype = {
26682     text : '', // string being parsed..
26683     controlWord : '',
26684     controlWordParam :  '',
26685     hexChar : '',
26686     doc : false,
26687     group: false,
26688     groupStack : false,
26689     hexStore : false,
26690     
26691     
26692     cpos : 0, 
26693     row : 1, // reportin?
26694     col : 1, //
26695
26696      
26697     push : function (el)
26698     {
26699         var m = 'cmd'+ el.type;
26700         if (typeof(this[m]) == 'undefined') {
26701             Roo.log('invalid cmd:' + el.type);
26702             return;
26703         }
26704         this[m](el);
26705         //Roo.log(el);
26706     },
26707     flushHexStore : function()
26708     {
26709         if (this.hexStore.length < 1) {
26710             return;
26711         }
26712         var hexstr = this.hexStore.map(
26713             function(cmd) {
26714                 return cmd.value;
26715         }).join('');
26716         
26717         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26718               
26719             
26720         this.hexStore.splice(0)
26721         
26722     },
26723     
26724     cmdgroupstart : function()
26725     {
26726         this.flushHexStore();
26727         if (this.group) {
26728             this.groupStack.push(this.group);
26729         }
26730          // parent..
26731         if (this.doc === false) {
26732             this.group = this.doc = new Roo.rtf.Document();
26733             return;
26734             
26735         }
26736         this.group = new Roo.rtf.Group(this.group);
26737     },
26738     cmdignorable : function()
26739     {
26740         this.flushHexStore();
26741         this.group.ignorable = true;
26742     },
26743     cmdendparagraph : function()
26744     {
26745         this.flushHexStore();
26746         this.group.addContent(new Roo.rtf.Paragraph());
26747     },
26748     cmdgroupend : function ()
26749     {
26750         this.flushHexStore();
26751         var endingGroup = this.group;
26752         
26753         
26754         this.group = this.groupStack.pop();
26755         if (this.group) {
26756             this.group.addChild(endingGroup);
26757         }
26758         
26759         
26760         
26761         var doc = this.group || this.doc;
26762         //if (endingGroup instanceof FontTable) {
26763         //  doc.fonts = endingGroup.table
26764         //} else if (endingGroup instanceof ColorTable) {
26765         //  doc.colors = endingGroup.table
26766         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26767         if (endingGroup.ignorable === false) {
26768             //code
26769             this.groups.push(endingGroup);
26770            // Roo.log( endingGroup );
26771         }
26772             //Roo.each(endingGroup.content, function(item)) {
26773             //    doc.addContent(item);
26774             //}
26775             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26776         //}
26777     },
26778     cmdtext : function (cmd)
26779     {
26780         this.flushHexStore();
26781         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26782             //this.group = this.doc
26783             return;  // we really don't care about stray text...
26784         }
26785         this.group.addContent(new Roo.rtf.Span(cmd));
26786     },
26787     cmdcontrolword : function (cmd)
26788     {
26789         this.flushHexStore();
26790         if (!this.group.type) {
26791             this.group.type = cmd.value;
26792             return;
26793         }
26794         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26795         // we actually don't care about ctrl words...
26796         return ;
26797         /*
26798         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26799         if (this[method]) {
26800             this[method](cmd.param)
26801         } else {
26802             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26803         }
26804         */
26805     },
26806     cmdhexchar : function(cmd) {
26807         this.hexStore.push(cmd);
26808     },
26809     cmderror : function(cmd) {
26810         throw cmd.value;
26811     },
26812     
26813     /*
26814       _flush (done) {
26815         if (this.text !== '\u0000') this.emitText()
26816         done()
26817       }
26818       */
26819       
26820       
26821     parseText : function(c)
26822     {
26823         if (c === '\\') {
26824             this.parserState = this.parseEscapes;
26825         } else if (c === '{') {
26826             this.emitStartGroup();
26827         } else if (c === '}') {
26828             this.emitEndGroup();
26829         } else if (c === '\x0A' || c === '\x0D') {
26830             // cr/lf are noise chars
26831         } else {
26832             this.text += c;
26833         }
26834     },
26835     
26836     parseEscapes: function (c)
26837     {
26838         if (c === '\\' || c === '{' || c === '}') {
26839             this.text += c;
26840             this.parserState = this.parseText;
26841         } else {
26842             this.parserState = this.parseControlSymbol;
26843             this.parseControlSymbol(c);
26844         }
26845     },
26846     parseControlSymbol: function(c)
26847     {
26848         if (c === '~') {
26849             this.text += '\u00a0'; // nbsp
26850             this.parserState = this.parseText
26851         } else if (c === '-') {
26852              this.text += '\u00ad'; // soft hyphen
26853         } else if (c === '_') {
26854             this.text += '\u2011'; // non-breaking hyphen
26855         } else if (c === '*') {
26856             this.emitIgnorable();
26857             this.parserState = this.parseText;
26858         } else if (c === "'") {
26859             this.parserState = this.parseHexChar;
26860         } else if (c === '|') { // formula cacter
26861             this.emitFormula();
26862             this.parserState = this.parseText;
26863         } else if (c === ':') { // subentry in an index entry
26864             this.emitIndexSubEntry();
26865             this.parserState = this.parseText;
26866         } else if (c === '\x0a') {
26867             this.emitEndParagraph();
26868             this.parserState = this.parseText;
26869         } else if (c === '\x0d') {
26870             this.emitEndParagraph();
26871             this.parserState = this.parseText;
26872         } else {
26873             this.parserState = this.parseControlWord;
26874             this.parseControlWord(c);
26875         }
26876     },
26877     parseHexChar: function (c)
26878     {
26879         if (/^[A-Fa-f0-9]$/.test(c)) {
26880             this.hexChar += c;
26881             if (this.hexChar.length >= 2) {
26882               this.emitHexChar();
26883               this.parserState = this.parseText;
26884             }
26885             return;
26886         }
26887         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26888         this.parserState = this.parseText;
26889         
26890     },
26891     parseControlWord : function(c)
26892     {
26893         if (c === ' ') {
26894             this.emitControlWord();
26895             this.parserState = this.parseText;
26896         } else if (/^[-\d]$/.test(c)) {
26897             this.parserState = this.parseControlWordParam;
26898             this.controlWordParam += c;
26899         } else if (/^[A-Za-z]$/.test(c)) {
26900           this.controlWord += c;
26901         } else {
26902           this.emitControlWord();
26903           this.parserState = this.parseText;
26904           this.parseText(c);
26905         }
26906     },
26907     parseControlWordParam : function (c) {
26908         if (/^\d$/.test(c)) {
26909           this.controlWordParam += c;
26910         } else if (c === ' ') {
26911           this.emitControlWord();
26912           this.parserState = this.parseText;
26913         } else {
26914           this.emitControlWord();
26915           this.parserState = this.parseText;
26916           this.parseText(c);
26917         }
26918     },
26919     
26920     
26921     
26922     
26923     emitText : function () {
26924         if (this.text === '') {
26925             return;
26926         }
26927         this.push({
26928             type: 'text',
26929             value: this.text,
26930             pos: this.cpos,
26931             row: this.row,
26932             col: this.col
26933         });
26934         this.text = ''
26935     },
26936     emitControlWord : function ()
26937     {
26938         this.emitText();
26939         if (this.controlWord === '') {
26940             // do we want to track this - it seems just to cause problems.
26941             //this.emitError('empty control word');
26942         } else {
26943             this.push({
26944                   type: 'controlword',
26945                   value: this.controlWord,
26946                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26947                   pos: this.cpos,
26948                   row: this.row,
26949                   col: this.col
26950             });
26951         }
26952         this.controlWord = '';
26953         this.controlWordParam = '';
26954     },
26955     emitStartGroup : function ()
26956     {
26957         this.emitText();
26958         this.push({
26959             type: 'groupstart',
26960             pos: this.cpos,
26961             row: this.row,
26962             col: this.col
26963         });
26964     },
26965     emitEndGroup : function ()
26966     {
26967         this.emitText();
26968         this.push({
26969             type: 'groupend',
26970             pos: this.cpos,
26971             row: this.row,
26972             col: this.col
26973         });
26974     },
26975     emitIgnorable : function ()
26976     {
26977         this.emitText();
26978         this.push({
26979             type: 'ignorable',
26980             pos: this.cpos,
26981             row: this.row,
26982             col: this.col
26983         });
26984     },
26985     emitHexChar : function ()
26986     {
26987         this.emitText();
26988         this.push({
26989             type: 'hexchar',
26990             value: this.hexChar,
26991             pos: this.cpos,
26992             row: this.row,
26993             col: this.col
26994         });
26995         this.hexChar = ''
26996     },
26997     emitError : function (message)
26998     {
26999       this.emitText();
27000       this.push({
27001             type: 'error',
27002             value: message,
27003             row: this.row,
27004             col: this.col,
27005             char: this.cpos //,
27006             //stack: new Error().stack
27007         });
27008     },
27009     emitEndParagraph : function () {
27010         this.emitText();
27011         this.push({
27012             type: 'endparagraph',
27013             pos: this.cpos,
27014             row: this.row,
27015             col: this.col
27016         });
27017     }
27018      
27019 } ; 
27020 /**
27021  * @class Roo.htmleditor.Filter
27022  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
27023  * @cfg {DomElement} node The node to iterate and filter
27024  * @cfg {boolean|String|Array} tag Tags to replace 
27025  * @constructor
27026  * Create a new Filter.
27027  * @param {Object} config Configuration options
27028  */
27029
27030
27031
27032 Roo.htmleditor.Filter = function(cfg) {
27033     Roo.apply(this.cfg);
27034     // this does not actually call walk as it's really just a abstract class
27035 }
27036
27037
27038 Roo.htmleditor.Filter.prototype = {
27039     
27040     node: false,
27041     
27042     tag: false,
27043
27044     // overrride to do replace comments.
27045     replaceComment : false,
27046     
27047     // overrride to do replace or do stuff with tags..
27048     replaceTag : false,
27049     
27050     walk : function(dom)
27051     {
27052         Roo.each( Array.from(dom.childNodes), function( e ) {
27053             switch(true) {
27054                 
27055                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
27056                     this.replaceComment(e);
27057                     return;
27058                 
27059                 case e.nodeType != 1: //not a node.
27060                     return;
27061                 
27062                 case this.tag === true: // everything
27063                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
27064                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
27065                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
27066                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
27067                     if (this.replaceTag && false === this.replaceTag(e)) {
27068                         return;
27069                     }
27070                     if (e.hasChildNodes()) {
27071                         this.walk(e);
27072                     }
27073                     return;
27074                 
27075                 default:    // tags .. that do not match.
27076                     if (e.hasChildNodes()) {
27077                         this.walk(e);
27078                     }
27079             }
27080             
27081         }, this);
27082         
27083     },
27084     
27085     
27086     removeNodeKeepChildren : function( node)
27087     {
27088     
27089         ar = Array.from(node.childNodes);
27090         for (var i = 0; i < ar.length; i++) {
27091          
27092             node.removeChild(ar[i]);
27093             // what if we need to walk these???
27094             node.parentNode.insertBefore(ar[i], node);
27095            
27096         }
27097         node.parentNode.removeChild(node);
27098     }
27099 }; 
27100
27101 /**
27102  * @class Roo.htmleditor.FilterAttributes
27103  * clean attributes and  styles including http:// etc.. in attribute
27104  * @constructor
27105 * Run a new Attribute Filter
27106 * @param {Object} config Configuration options
27107  */
27108 Roo.htmleditor.FilterAttributes = function(cfg)
27109 {
27110     Roo.apply(this, cfg);
27111     this.attrib_black = this.attrib_black || [];
27112     this.attrib_white = this.attrib_white || [];
27113
27114     this.attrib_clean = this.attrib_clean || [];
27115     this.style_white = this.style_white || [];
27116     this.style_black = this.style_black || [];
27117     this.walk(cfg.node);
27118 }
27119
27120 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
27121 {
27122     tag: true, // all tags
27123     
27124     attrib_black : false, // array
27125     attrib_clean : false,
27126     attrib_white : false,
27127
27128     style_white : false,
27129     style_black : false,
27130      
27131      
27132     replaceTag : function(node)
27133     {
27134         if (!node.attributes || !node.attributes.length) {
27135             return true;
27136         }
27137         
27138         for (var i = node.attributes.length-1; i > -1 ; i--) {
27139             var a = node.attributes[i];
27140             //console.log(a);
27141             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
27142                 node.removeAttribute(a.name);
27143                 continue;
27144             }
27145             
27146             
27147             
27148             if (a.name.toLowerCase().substr(0,2)=='on')  {
27149                 node.removeAttribute(a.name);
27150                 continue;
27151             }
27152             
27153             
27154             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
27155                 node.removeAttribute(a.name);
27156                 continue;
27157             }
27158             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
27159                 this.cleanAttr(node,a.name,a.value); // fixme..
27160                 continue;
27161             }
27162             if (a.name == 'style') {
27163                 this.cleanStyle(node,a.name,a.value);
27164                 continue;
27165             }
27166             /// clean up MS crap..
27167             // tecnically this should be a list of valid class'es..
27168             
27169             
27170             if (a.name == 'class') {
27171                 if (a.value.match(/^Mso/)) {
27172                     node.removeAttribute('class');
27173                 }
27174                 
27175                 if (a.value.match(/^body$/)) {
27176                     node.removeAttribute('class');
27177                 }
27178                 continue;
27179             }
27180             
27181             
27182             // style cleanup!?
27183             // class cleanup?
27184             
27185         }
27186         return true; // clean children
27187     },
27188         
27189     cleanAttr: function(node, n,v)
27190     {
27191         
27192         if (v.match(/^\./) || v.match(/^\//)) {
27193             return;
27194         }
27195         if (v.match(/^(http|https):\/\//)
27196             || v.match(/^mailto:/) 
27197             || v.match(/^ftp:/)
27198             || v.match(/^data:/)
27199             ) {
27200             return;
27201         }
27202         if (v.match(/^#/)) {
27203             return;
27204         }
27205         if (v.match(/^\{/)) { // allow template editing.
27206             return;
27207         }
27208 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27209         node.removeAttribute(n);
27210         
27211     },
27212     cleanStyle : function(node,  n,v)
27213     {
27214         if (v.match(/expression/)) { //XSS?? should we even bother..
27215             node.removeAttribute(n);
27216             return;
27217         }
27218         
27219         var parts = v.split(/;/);
27220         var clean = [];
27221         
27222         Roo.each(parts, function(p) {
27223             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27224             if (!p.length) {
27225                 return true;
27226             }
27227             var l = p.split(':').shift().replace(/\s+/g,'');
27228             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27229             
27230             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
27231                 return true;
27232             }
27233             //Roo.log()
27234             // only allow 'c whitelisted system attributes'
27235             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
27236                 return true;
27237             }
27238             
27239             
27240             clean.push(p);
27241             return true;
27242         },this);
27243         if (clean.length) { 
27244             node.setAttribute(n, clean.join(';'));
27245         } else {
27246             node.removeAttribute(n);
27247         }
27248         
27249     }
27250         
27251         
27252         
27253     
27254 });/**
27255  * @class Roo.htmleditor.FilterBlack
27256  * remove blacklisted elements.
27257  * @constructor
27258  * Run a new Blacklisted Filter
27259  * @param {Object} config Configuration options
27260  */
27261
27262 Roo.htmleditor.FilterBlack = function(cfg)
27263 {
27264     Roo.apply(this, cfg);
27265     this.walk(cfg.node);
27266 }
27267
27268 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
27269 {
27270     tag : true, // all elements.
27271    
27272     replaceTag : function(n)
27273     {
27274         n.parentNode.removeChild(n);
27275     }
27276 });
27277 /**
27278  * @class Roo.htmleditor.FilterComment
27279  * remove comments.
27280  * @constructor
27281 * Run a new Comments Filter
27282 * @param {Object} config Configuration options
27283  */
27284 Roo.htmleditor.FilterComment = function(cfg)
27285 {
27286     this.walk(cfg.node);
27287 }
27288
27289 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
27290 {
27291   
27292     replaceComment : function(n)
27293     {
27294         n.parentNode.removeChild(n);
27295     }
27296 });/**
27297  * @class Roo.htmleditor.FilterKeepChildren
27298  * remove tags but keep children
27299  * @constructor
27300  * Run a new Keep Children Filter
27301  * @param {Object} config Configuration options
27302  */
27303
27304 Roo.htmleditor.FilterKeepChildren = function(cfg)
27305 {
27306     Roo.apply(this, cfg);
27307     if (this.tag === false) {
27308         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
27309     }
27310     // hacky?
27311     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
27312         this.cleanNamespace = true;
27313     }
27314         
27315     this.walk(cfg.node);
27316 }
27317
27318 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
27319 {
27320     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
27321   
27322     replaceTag : function(node)
27323     {
27324         // walk children...
27325         //Roo.log(node.tagName);
27326         var ar = Array.from(node.childNodes);
27327         //remove first..
27328         
27329         for (var i = 0; i < ar.length; i++) {
27330             var e = ar[i];
27331             if (e.nodeType == 1) {
27332                 if (
27333                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
27334                     || // array and it matches
27335                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
27336                     ||
27337                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
27338                     ||
27339                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
27340                 ) {
27341                     this.replaceTag(ar[i]); // child is blacklisted as well...
27342                     continue;
27343                 }
27344             }
27345         }  
27346         ar = Array.from(node.childNodes);
27347         for (var i = 0; i < ar.length; i++) {
27348          
27349             node.removeChild(ar[i]);
27350             // what if we need to walk these???
27351             node.parentNode.insertBefore(ar[i], node);
27352             if (this.tag !== false) {
27353                 this.walk(ar[i]);
27354                 
27355             }
27356         }
27357         //Roo.log("REMOVE:" + node.tagName);
27358         node.parentNode.removeChild(node);
27359         return false; // don't walk children
27360         
27361         
27362     }
27363 });/**
27364  * @class Roo.htmleditor.FilterParagraph
27365  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
27366  * like on 'push' to remove the <p> tags and replace them with line breaks.
27367  * @constructor
27368  * Run a new Paragraph Filter
27369  * @param {Object} config Configuration options
27370  */
27371
27372 Roo.htmleditor.FilterParagraph = function(cfg)
27373 {
27374     // no need to apply config.
27375     this.walk(cfg.node);
27376 }
27377
27378 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
27379 {
27380     
27381      
27382     tag : 'P',
27383     
27384      
27385     replaceTag : function(node)
27386     {
27387         
27388         if (node.childNodes.length == 1 &&
27389             node.childNodes[0].nodeType == 3 &&
27390             node.childNodes[0].textContent.trim().length < 1
27391             ) {
27392             // remove and replace with '<BR>';
27393             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
27394             return false; // no need to walk..
27395         }
27396         var ar = Array.from(node.childNodes);
27397         for (var i = 0; i < ar.length; i++) {
27398             node.removeChild(ar[i]);
27399             // what if we need to walk these???
27400             node.parentNode.insertBefore(ar[i], node);
27401         }
27402         // now what about this?
27403         // <p> &nbsp; </p>
27404         
27405         // double BR.
27406         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
27407         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
27408         node.parentNode.removeChild(node);
27409         
27410         return false;
27411
27412     }
27413     
27414 });/**
27415  * @class Roo.htmleditor.FilterSpan
27416  * filter span's with no attributes out..
27417  * @constructor
27418  * Run a new Span Filter
27419  * @param {Object} config Configuration options
27420  */
27421
27422 Roo.htmleditor.FilterSpan = function(cfg)
27423 {
27424     // no need to apply config.
27425     this.walk(cfg.node);
27426 }
27427
27428 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
27429 {
27430      
27431     tag : 'SPAN',
27432      
27433  
27434     replaceTag : function(node)
27435     {
27436         if (node.attributes && node.attributes.length > 0) {
27437             return true; // walk if there are any.
27438         }
27439         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
27440         return false;
27441      
27442     }
27443     
27444 });/**
27445  * @class Roo.htmleditor.FilterTableWidth
27446   try and remove table width data - as that frequently messes up other stuff.
27447  * 
27448  *      was cleanTableWidths.
27449  *
27450  * Quite often pasting from word etc.. results in tables with column and widths.
27451  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27452  *
27453  * @constructor
27454  * Run a new Table Filter
27455  * @param {Object} config Configuration options
27456  */
27457
27458 Roo.htmleditor.FilterTableWidth = function(cfg)
27459 {
27460     // no need to apply config.
27461     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
27462     this.walk(cfg.node);
27463 }
27464
27465 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
27466 {
27467      
27468      
27469     
27470     replaceTag: function(node) {
27471         
27472         
27473       
27474         if (node.hasAttribute('width')) {
27475             node.removeAttribute('width');
27476         }
27477         
27478          
27479         if (node.hasAttribute("style")) {
27480             // pretty basic...
27481             
27482             var styles = node.getAttribute("style").split(";");
27483             var nstyle = [];
27484             Roo.each(styles, function(s) {
27485                 if (!s.match(/:/)) {
27486                     return;
27487                 }
27488                 var kv = s.split(":");
27489                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27490                     return;
27491                 }
27492                 // what ever is left... we allow.
27493                 nstyle.push(s);
27494             });
27495             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27496             if (!nstyle.length) {
27497                 node.removeAttribute('style');
27498             }
27499         }
27500         
27501         return true; // continue doing children..
27502     }
27503 });/**
27504  * @class Roo.htmleditor.FilterWord
27505  * try and clean up all the mess that Word generates.
27506  * 
27507  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
27508  
27509  * @constructor
27510  * Run a new Span Filter
27511  * @param {Object} config Configuration options
27512  */
27513
27514 Roo.htmleditor.FilterWord = function(cfg)
27515 {
27516     // no need to apply config.
27517     this.replaceDocBullets(cfg.node);
27518     
27519     this.replaceAname(cfg.node);
27520     // this is disabled as the removal is done by other filters;
27521    // this.walk(cfg.node);
27522     this.replaceImageTable(cfg.node);
27523     
27524 }
27525
27526 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27527 {
27528     tag: true,
27529      
27530     
27531     /**
27532      * Clean up MS wordisms...
27533      */
27534     replaceTag : function(node)
27535     {
27536          
27537         // no idea what this does - span with text, replaceds with just text.
27538         if(
27539                 node.nodeName == 'SPAN' &&
27540                 !node.hasAttributes() &&
27541                 node.childNodes.length == 1 &&
27542                 node.firstChild.nodeName == "#text"  
27543         ) {
27544             var textNode = node.firstChild;
27545             node.removeChild(textNode);
27546             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27547                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27548             }
27549             node.parentNode.insertBefore(textNode, node);
27550             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27551                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27552             }
27553             
27554             node.parentNode.removeChild(node);
27555             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27556         }
27557         
27558    
27559         
27560         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27561             node.parentNode.removeChild(node);
27562             return false; // dont do chidlren
27563         }
27564         //Roo.log(node.tagName);
27565         // remove - but keep children..
27566         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27567             //Roo.log('-- removed');
27568             while (node.childNodes.length) {
27569                 var cn = node.childNodes[0];
27570                 node.removeChild(cn);
27571                 node.parentNode.insertBefore(cn, node);
27572                 // move node to parent - and clean it..
27573                 if (cn.nodeType == 1) {
27574                     this.replaceTag(cn);
27575                 }
27576                 
27577             }
27578             node.parentNode.removeChild(node);
27579             /// no need to iterate chidlren = it's got none..
27580             //this.iterateChildren(node, this.cleanWord);
27581             return false; // no need to iterate children.
27582         }
27583         // clean styles
27584         if (node.className.length) {
27585             
27586             var cn = node.className.split(/\W+/);
27587             var cna = [];
27588             Roo.each(cn, function(cls) {
27589                 if (cls.match(/Mso[a-zA-Z]+/)) {
27590                     return;
27591                 }
27592                 cna.push(cls);
27593             });
27594             node.className = cna.length ? cna.join(' ') : '';
27595             if (!cna.length) {
27596                 node.removeAttribute("class");
27597             }
27598         }
27599         
27600         if (node.hasAttribute("lang")) {
27601             node.removeAttribute("lang");
27602         }
27603         
27604         if (node.hasAttribute("style")) {
27605             
27606             var styles = node.getAttribute("style").split(";");
27607             var nstyle = [];
27608             Roo.each(styles, function(s) {
27609                 if (!s.match(/:/)) {
27610                     return;
27611                 }
27612                 var kv = s.split(":");
27613                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27614                     return;
27615                 }
27616                 // what ever is left... we allow.
27617                 nstyle.push(s);
27618             });
27619             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27620             if (!nstyle.length) {
27621                 node.removeAttribute('style');
27622             }
27623         }
27624         return true; // do children
27625         
27626         
27627         
27628     },
27629     
27630     styleToObject: function(node)
27631     {
27632         var styles = (node.getAttribute("style") || '').split(";");
27633         var ret = {};
27634         Roo.each(styles, function(s) {
27635             if (!s.match(/:/)) {
27636                 return;
27637             }
27638             var kv = s.split(":");
27639              
27640             // what ever is left... we allow.
27641             ret[kv[0].trim()] = kv[1];
27642         });
27643         return ret;
27644     },
27645     
27646     
27647     replaceAname : function (doc)
27648     {
27649         // replace all the a/name without..
27650         var aa = Array.from(doc.getElementsByTagName('a'));
27651         for (var i = 0; i  < aa.length; i++) {
27652             var a = aa[i];
27653             if (a.hasAttribute("name")) {
27654                 a.removeAttribute("name");
27655             }
27656             if (a.hasAttribute("href")) {
27657                 continue;
27658             }
27659             // reparent children.
27660             this.removeNodeKeepChildren(a);
27661             
27662         }
27663         
27664         
27665         
27666     },
27667
27668     
27669     
27670     replaceDocBullets : function(doc)
27671     {
27672         // this is a bit odd - but it appears some indents use ql-indent-1
27673          //Roo.log(doc.innerHTML);
27674         
27675         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27676         for( var i = 0; i < listpara.length; i ++) {
27677             listpara[i].className = "MsoListParagraph";
27678         }
27679         
27680         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27681         for( var i = 0; i < listpara.length; i ++) {
27682             listpara[i].className = "MsoListParagraph";
27683         }
27684         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27685         for( var i = 0; i < listpara.length; i ++) {
27686             listpara[i].className = "MsoListParagraph";
27687         }
27688         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27689         for( var i = 0; i < listpara.length; i ++) {
27690             listpara[i].className = "MsoListParagraph";
27691         }
27692         
27693         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27694         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27695         for( var i = 0; i < htwo.length; i ++) {
27696             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27697                 htwo[i].className = "MsoListParagraph";
27698             }
27699         }
27700         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27701         for( var i = 0; i < listpara.length; i ++) {
27702             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27703                 listpara[i].className = "MsoListParagraph";
27704             } else {
27705                 listpara[i].className = "MsoNormalx";
27706             }
27707         }
27708        
27709         listpara = doc.getElementsByClassName('MsoListParagraph');
27710         // Roo.log(doc.innerHTML);
27711         
27712         
27713         
27714         while(listpara.length) {
27715             
27716             this.replaceDocBullet(listpara.item(0));
27717         }
27718       
27719     },
27720     
27721      
27722     
27723     replaceDocBullet : function(p)
27724     {
27725         // gather all the siblings.
27726         var ns = p,
27727             parent = p.parentNode,
27728             doc = parent.ownerDocument,
27729             items = [];
27730          
27731         //Roo.log("Parsing: " + p.innerText)    ;
27732         var listtype = 'ul';   
27733         while (ns) {
27734             if (ns.nodeType != 1) {
27735                 ns = ns.nextSibling;
27736                 continue;
27737             }
27738             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27739                 //Roo.log("Missing para r q1indent - got:" + ns.className);
27740                 break;
27741             }
27742             var spans = ns.getElementsByTagName('span');
27743             
27744             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27745                 items.push(ns);
27746                 ns = ns.nextSibling;
27747                 has_list = true;
27748                 if (!spans.length) {
27749                     continue;
27750                 }
27751                 var ff = '';
27752                 var se = spans[0];
27753                 for (var i = 0; i < spans.length;i++) {
27754                     se = spans[i];
27755                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
27756                         ff = se.style.fontFamily;
27757                         break;
27758                     }
27759                 }
27760                  
27761                     
27762                 //Roo.log("got font family: " + ff);
27763                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27764                     listtype = 'ol';
27765                 }
27766                 
27767                 continue;
27768             }
27769             //Roo.log("no mso-list?");
27770             
27771             var spans = ns.getElementsByTagName('span');
27772             if (!spans.length) {
27773                 break;
27774             }
27775             var has_list  = false;
27776             for(var i = 0; i < spans.length; i++) {
27777                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27778                     has_list = true;
27779                     break;
27780                 }
27781             }
27782             if (!has_list) {
27783                 break;
27784             }
27785             items.push(ns);
27786             ns = ns.nextSibling;
27787             
27788             
27789         }
27790         if (!items.length) {
27791             ns.className = "";
27792             return;
27793         }
27794         
27795         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27796         parent.insertBefore(ul, p);
27797         var lvl = 0;
27798         var stack = [ ul ];
27799         var last_li = false;
27800         
27801         var margin_to_depth = {};
27802         max_margins = -1;
27803         
27804         items.forEach(function(n, ipos) {
27805             //Roo.log("got innertHMLT=" + n.innerHTML);
27806             
27807             var spans = n.getElementsByTagName('span');
27808             if (!spans.length) {
27809                 //Roo.log("No spans found");
27810                  
27811                 parent.removeChild(n);
27812                 
27813                 
27814                 return; // skip it...
27815             }
27816            
27817                 
27818             var num = 1;
27819             var style = {};
27820             for(var i = 0; i < spans.length; i++) {
27821             
27822                 style = this.styleToObject(spans[i]);
27823                 if (typeof(style['mso-list']) == 'undefined') {
27824                     continue;
27825                 }
27826                 if (listtype == 'ol') {
27827                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27828                 }
27829                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27830                 break;
27831             }
27832             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27833             style = this.styleToObject(n); // mo-list is from the parent node.
27834             if (typeof(style['mso-list']) == 'undefined') {
27835                 //Roo.log("parent is missing level");
27836                   
27837                 parent.removeChild(n);
27838                  
27839                 return;
27840             }
27841             
27842             var margin = style['margin-left'];
27843             if (typeof(margin_to_depth[margin]) == 'undefined') {
27844                 max_margins++;
27845                 margin_to_depth[margin] = max_margins;
27846             }
27847             nlvl = margin_to_depth[margin] ;
27848              
27849             if (nlvl > lvl) {
27850                 //new indent
27851                 var nul = doc.createElement(listtype); // what about number lists...
27852                 if (!last_li) {
27853                     last_li = doc.createElement('li');
27854                     stack[lvl].appendChild(last_li);
27855                 }
27856                 last_li.appendChild(nul);
27857                 stack[nlvl] = nul;
27858                 
27859             }
27860             lvl = nlvl;
27861             
27862             // not starting at 1..
27863             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27864                 stack[nlvl].setAttribute("start", num);
27865             }
27866             
27867             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27868             last_li = nli;
27869             nli.innerHTML = n.innerHTML;
27870             //Roo.log("innerHTML = " + n.innerHTML);
27871             parent.removeChild(n);
27872             
27873              
27874              
27875             
27876         },this);
27877         
27878         
27879         
27880         
27881     },
27882     
27883     replaceImageTable : function(doc)
27884     {
27885          /*
27886           <table cellpadding=0 cellspacing=0 align=left>
27887   <tr>
27888    <td width=423 height=0></td>
27889   </tr>
27890   <tr>
27891    <td></td>
27892    <td><img width=601 height=401
27893    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27894    v:shapes="Picture_x0020_2"></td>
27895   </tr>
27896  </table>
27897  */
27898         var imgs = Array.from(doc.getElementsByTagName('img'));
27899         Roo.each(imgs, function(img) {
27900             var td = img.parentNode;
27901             if (td.nodeName !=  'TD') {
27902                 return;
27903             }
27904             var tr = td.parentNode;
27905             if (tr.nodeName !=  'TR') {
27906                 return;
27907             }
27908             var tbody = tr.parentNode;
27909             if (tbody.nodeName !=  'TBODY') {
27910                 return;
27911             }
27912             var table = tbody.parentNode;
27913             if (table.nodeName !=  'TABLE') {
27914                 return;
27915             }
27916             // first row..
27917             
27918             if (table.getElementsByTagName('tr').length != 2) {
27919                 return;
27920             }
27921             if (table.getElementsByTagName('td').length != 3) {
27922                 return;
27923             }
27924             if (table.innerText.trim() != '') {
27925                 return;
27926             }
27927             var p = table.parentNode;
27928             img.parentNode.removeChild(img);
27929             p.insertBefore(img, table);
27930             p.removeChild(table);
27931             
27932             
27933             
27934         });
27935         
27936       
27937     }
27938     
27939 });
27940 /**
27941  * @class Roo.htmleditor.FilterStyleToTag
27942  * part of the word stuff... - certain 'styles' should be converted to tags.
27943  * eg.
27944  *   font-weight: bold -> bold
27945  *   ?? super / subscrit etc..
27946  * 
27947  * @constructor
27948 * Run a new style to tag filter.
27949 * @param {Object} config Configuration options
27950  */
27951 Roo.htmleditor.FilterStyleToTag = function(cfg)
27952 {
27953     
27954     this.tags = {
27955         B  : [ 'fontWeight' , 'bold'],
27956         I :  [ 'fontStyle' , 'italic'],
27957         //pre :  [ 'font-style' , 'italic'],
27958         // h1.. h6 ?? font-size?
27959         SUP : [ 'verticalAlign' , 'super' ],
27960         SUB : [ 'verticalAlign' , 'sub' ]
27961         
27962         
27963     };
27964     
27965     Roo.apply(this, cfg);
27966      
27967     
27968     this.walk(cfg.node);
27969     
27970     
27971     
27972 }
27973
27974
27975 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27976 {
27977     tag: true, // all tags
27978     
27979     tags : false,
27980     
27981     
27982     replaceTag : function(node)
27983     {
27984         
27985         
27986         if (node.getAttribute("style") === null) {
27987             return true;
27988         }
27989         var inject = [];
27990         for (var k in this.tags) {
27991             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27992                 inject.push(k);
27993                 node.style.removeProperty(this.tags[k][0]);
27994             }
27995         }
27996         if (!inject.length) {
27997             return true; 
27998         }
27999         var cn = Array.from(node.childNodes);
28000         var nn = node;
28001         Roo.each(inject, function(t) {
28002             var nc = node.ownerDocument.createElement(t);
28003             nn.appendChild(nc);
28004             nn = nc;
28005         });
28006         for(var i = 0;i < cn.length;cn++) {
28007             node.removeChild(cn[i]);
28008             nn.appendChild(cn[i]);
28009         }
28010         return true /// iterate thru
28011     }
28012     
28013 })/**
28014  * @class Roo.htmleditor.FilterLongBr
28015  * BR/BR/BR - keep a maximum of 2...
28016  * @constructor
28017  * Run a new Long BR Filter
28018  * @param {Object} config Configuration options
28019  */
28020
28021 Roo.htmleditor.FilterLongBr = function(cfg)
28022 {
28023     // no need to apply config.
28024     this.walk(cfg.node);
28025 }
28026
28027 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
28028 {
28029     
28030      
28031     tag : 'BR',
28032     
28033      
28034     replaceTag : function(node)
28035     {
28036         
28037         var ps = node.nextSibling;
28038         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
28039             ps = ps.nextSibling;
28040         }
28041         
28042         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
28043             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
28044             return false;
28045         }
28046         
28047         if (!ps || ps.nodeType != 1) {
28048             return false;
28049         }
28050         
28051         if (!ps || ps.tagName != 'BR') {
28052            
28053             return false;
28054         }
28055         
28056         
28057         
28058         
28059         
28060         if (!node.previousSibling) {
28061             return false;
28062         }
28063         var ps = node.previousSibling;
28064         
28065         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
28066             ps = ps.previousSibling;
28067         }
28068         if (!ps || ps.nodeType != 1) {
28069             return false;
28070         }
28071         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
28072         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
28073             return false;
28074         }
28075         
28076         node.parentNode.removeChild(node); // remove me...
28077         
28078         return false; // no need to do children
28079
28080     }
28081     
28082 }); 
28083
28084 /**
28085  * @class Roo.htmleditor.FilterBlock
28086  * removes id / data-block and contenteditable that are associated with blocks
28087  * usage should be done on a cloned copy of the dom
28088  * @constructor
28089 * Run a new Attribute Filter { node : xxxx }}
28090 * @param {Object} config Configuration options
28091  */
28092 Roo.htmleditor.FilterBlock = function(cfg)
28093 {
28094     Roo.apply(this, cfg);
28095     var qa = cfg.node.querySelectorAll;
28096     this.removeAttributes('data-block');
28097     this.removeAttributes('contenteditable');
28098     this.removeAttributes('id');
28099     
28100 }
28101
28102 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
28103 {
28104     node: true, // all tags
28105      
28106      
28107     removeAttributes : function(attr)
28108     {
28109         var ar = this.node.querySelectorAll('*[' + attr + ']');
28110         for (var i =0;i<ar.length;i++) {
28111             ar[i].removeAttribute(attr);
28112         }
28113     }
28114         
28115         
28116         
28117     
28118 });
28119 /***
28120  * This is based loosely on tinymce 
28121  * @class Roo.htmleditor.TidySerializer
28122  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28123  * @constructor
28124  * @method Serializer
28125  * @param {Object} settings Name/value settings object.
28126  */
28127
28128
28129 Roo.htmleditor.TidySerializer = function(settings)
28130 {
28131     Roo.apply(this, settings);
28132     
28133     this.writer = new Roo.htmleditor.TidyWriter(settings);
28134     
28135     
28136
28137 };
28138 Roo.htmleditor.TidySerializer.prototype = {
28139     
28140     /**
28141      * @param {boolean} inner do the inner of the node.
28142      */
28143     inner : false,
28144     
28145     writer : false,
28146     
28147     /**
28148     * Serializes the specified node into a string.
28149     *
28150     * @example
28151     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
28152     * @method serialize
28153     * @param {DomElement} node Node instance to serialize.
28154     * @return {String} String with HTML based on DOM tree.
28155     */
28156     serialize : function(node) {
28157         
28158         // = settings.validate;
28159         var writer = this.writer;
28160         var self  = this;
28161         this.handlers = {
28162             // #text
28163             3: function(node) {
28164                 
28165                 writer.text(node.nodeValue, node);
28166             },
28167             // #comment
28168             8: function(node) {
28169                 writer.comment(node.nodeValue);
28170             },
28171             // Processing instruction
28172             7: function(node) {
28173                 writer.pi(node.name, node.nodeValue);
28174             },
28175             // Doctype
28176             10: function(node) {
28177                 writer.doctype(node.nodeValue);
28178             },
28179             // CDATA
28180             4: function(node) {
28181                 writer.cdata(node.nodeValue);
28182             },
28183             // Document fragment
28184             11: function(node) {
28185                 node = node.firstChild;
28186                 if (!node) {
28187                     return;
28188                 }
28189                 while(node) {
28190                     self.walk(node);
28191                     node = node.nextSibling
28192                 }
28193             }
28194         };
28195         writer.reset();
28196         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
28197         return writer.getContent();
28198     },
28199
28200     walk: function(node)
28201     {
28202         var attrName, attrValue, sortedAttrs, i, l, elementRule,
28203             handler = this.handlers[node.nodeType];
28204             
28205         if (handler) {
28206             handler(node);
28207             return;
28208         }
28209     
28210         var name = node.nodeName;
28211         var isEmpty = node.childNodes.length < 1;
28212       
28213         var writer = this.writer;
28214         var attrs = node.attributes;
28215         // Sort attributes
28216         
28217         writer.start(node.nodeName, attrs, isEmpty, node);
28218         if (isEmpty) {
28219             return;
28220         }
28221         node = node.firstChild;
28222         if (!node) {
28223             writer.end(name);
28224             return;
28225         }
28226         while (node) {
28227             this.walk(node);
28228             node = node.nextSibling;
28229         }
28230         writer.end(name);
28231         
28232     
28233     }
28234     // Serialize element and treat all non elements as fragments
28235    
28236 }; 
28237
28238 /***
28239  * This is based loosely on tinymce 
28240  * @class Roo.htmleditor.TidyWriter
28241  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28242  *
28243  * Known issues?
28244  * - not tested much with 'PRE' formated elements.
28245  * 
28246  *
28247  *
28248  */
28249
28250 Roo.htmleditor.TidyWriter = function(settings)
28251 {
28252     
28253     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
28254     Roo.apply(this, settings);
28255     this.html = [];
28256     this.state = [];
28257      
28258     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
28259   
28260 }
28261 Roo.htmleditor.TidyWriter.prototype = {
28262
28263  
28264     state : false,
28265     
28266     indent :  '  ',
28267     
28268     // part of state...
28269     indentstr : '',
28270     in_pre: false,
28271     in_inline : false,
28272     last_inline : false,
28273     encode : false,
28274      
28275     
28276             /**
28277     * Writes the a start element such as <p id="a">.
28278     *
28279     * @method start
28280     * @param {String} name Name of the element.
28281     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
28282     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
28283     */
28284     start: function(name, attrs, empty, node)
28285     {
28286         var i, l, attr, value;
28287         
28288         // there are some situations where adding line break && indentation will not work. will not work.
28289         // <span / b / i ... formating?
28290         
28291         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
28292         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
28293         
28294         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
28295         
28296         var add_lb = name == 'BR' ? false : in_inline;
28297         
28298         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
28299             i_inline = false;
28300         }
28301
28302         var indentstr =  this.indentstr;
28303         
28304         // e_inline = elements that can be inline, but still allow \n before and after?
28305         // only 'BR' ??? any others?
28306         
28307         // ADD LINE BEFORE tage
28308         if (!this.in_pre) {
28309             if (in_inline) {
28310                 //code
28311                 if (name == 'BR') {
28312                     this.addLine();
28313                 } else if (this.lastElementEndsWS()) {
28314                     this.addLine();
28315                 } else{
28316                     // otherwise - no new line. (and dont indent.)
28317                     indentstr = '';
28318                 }
28319                 
28320             } else {
28321                 this.addLine();
28322             }
28323         } else {
28324             indentstr = '';
28325         }
28326         
28327         this.html.push(indentstr + '<', name.toLowerCase());
28328         
28329         if (attrs) {
28330             for (i = 0, l = attrs.length; i < l; i++) {
28331                 attr = attrs[i];
28332                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
28333             }
28334         }
28335      
28336         if (empty) {
28337             if (is_short) {
28338                 this.html[this.html.length] = '/>';
28339             } else {
28340                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
28341             }
28342             var e_inline = name == 'BR' ? false : this.in_inline;
28343             
28344             if (!e_inline && !this.in_pre) {
28345                 this.addLine();
28346             }
28347             return;
28348         
28349         }
28350         // not empty..
28351         this.html[this.html.length] = '>';
28352         
28353         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
28354         /*
28355         if (!in_inline && !in_pre) {
28356             var cn = node.firstChild;
28357             while(cn) {
28358                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
28359                     in_inline = true
28360                     break;
28361                 }
28362                 cn = cn.nextSibling;
28363             }
28364              
28365         }
28366         */
28367         
28368         
28369         this.pushState({
28370             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
28371             in_pre : in_pre,
28372             in_inline :  in_inline
28373         });
28374         // add a line after if we are not in a
28375         
28376         if (!in_inline && !in_pre) {
28377             this.addLine();
28378         }
28379         
28380             
28381          
28382         
28383     },
28384     
28385     lastElementEndsWS : function()
28386     {
28387         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
28388         if (value === false) {
28389             return true;
28390         }
28391         return value.match(/\s+$/);
28392         
28393     },
28394     
28395     /**
28396      * Writes the a end element such as </p>.
28397      *
28398      * @method end
28399      * @param {String} name Name of the element.
28400      */
28401     end: function(name) {
28402         var value;
28403         this.popState();
28404         var indentstr = '';
28405         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
28406         
28407         if (!this.in_pre && !in_inline) {
28408             this.addLine();
28409             indentstr  = this.indentstr;
28410         }
28411         this.html.push(indentstr + '</', name.toLowerCase(), '>');
28412         this.last_inline = in_inline;
28413         
28414         // pop the indent state..
28415     },
28416     /**
28417      * Writes a text node.
28418      *
28419      * In pre - we should not mess with the contents.
28420      * 
28421      *
28422      * @method text
28423      * @param {String} text String to write out.
28424      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
28425      */
28426     text: function(in_text, node)
28427     {
28428         // if not in whitespace critical
28429         if (in_text.length < 1) {
28430             return;
28431         }
28432         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
28433         
28434         if (this.in_pre) {
28435             this.html[this.html.length] =  text;
28436             return;   
28437         }
28438         
28439         if (this.in_inline) {
28440             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
28441             if (text != ' ') {
28442                 text = text.replace(/\s+/,' ');  // all white space to single white space
28443                 
28444                     
28445                 // if next tag is '<BR>', then we can trim right..
28446                 if (node.nextSibling &&
28447                     node.nextSibling.nodeType == 1 &&
28448                     node.nextSibling.nodeName == 'BR' )
28449                 {
28450                     text = text.replace(/\s+$/g,'');
28451                 }
28452                 // if previous tag was a BR, we can also trim..
28453                 if (node.previousSibling &&
28454                     node.previousSibling.nodeType == 1 &&
28455                     node.previousSibling.nodeName == 'BR' )
28456                 {
28457                     text = this.indentstr +  text.replace(/^\s+/g,'');
28458                 }
28459                 if (text.match(/\n/)) {
28460                     text = text.replace(
28461                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28462                     );
28463                     // remoeve the last whitespace / line break.
28464                     text = text.replace(/\n\s+$/,'');
28465                 }
28466                 // repace long lines
28467                 
28468             }
28469              
28470             this.html[this.html.length] =  text;
28471             return;   
28472         }
28473         // see if previous element was a inline element.
28474         var indentstr = this.indentstr;
28475    
28476         text = text.replace(/\s+/g," "); // all whitespace into single white space.
28477         
28478         // should trim left?
28479         if (node.previousSibling &&
28480             node.previousSibling.nodeType == 1 &&
28481             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
28482         {
28483             indentstr = '';
28484             
28485         } else {
28486             this.addLine();
28487             text = text.replace(/^\s+/,''); // trim left
28488           
28489         }
28490         // should trim right?
28491         if (node.nextSibling &&
28492             node.nextSibling.nodeType == 1 &&
28493             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
28494         {
28495           // noop
28496             
28497         }  else {
28498             text = text.replace(/\s+$/,''); // trim right
28499         }
28500          
28501               
28502         
28503         
28504         
28505         if (text.length < 1) {
28506             return;
28507         }
28508         if (!text.match(/\n/)) {
28509             this.html.push(indentstr + text);
28510             return;
28511         }
28512         
28513         text = this.indentstr + text.replace(
28514             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28515         );
28516         // remoeve the last whitespace / line break.
28517         text = text.replace(/\s+$/,''); 
28518         
28519         this.html.push(text);
28520         
28521         // split and indent..
28522         
28523         
28524     },
28525     /**
28526      * Writes a cdata node such as <![CDATA[data]]>.
28527      *
28528      * @method cdata
28529      * @param {String} text String to write out inside the cdata.
28530      */
28531     cdata: function(text) {
28532         this.html.push('<![CDATA[', text, ']]>');
28533     },
28534     /**
28535     * Writes a comment node such as <!-- Comment -->.
28536     *
28537     * @method cdata
28538     * @param {String} text String to write out inside the comment.
28539     */
28540    comment: function(text) {
28541        this.html.push('<!--', text, '-->');
28542    },
28543     /**
28544      * Writes a PI node such as <?xml attr="value" ?>.
28545      *
28546      * @method pi
28547      * @param {String} name Name of the pi.
28548      * @param {String} text String to write out inside the pi.
28549      */
28550     pi: function(name, text) {
28551         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28552         this.indent != '' && this.html.push('\n');
28553     },
28554     /**
28555      * Writes a doctype node such as <!DOCTYPE data>.
28556      *
28557      * @method doctype
28558      * @param {String} text String to write out inside the doctype.
28559      */
28560     doctype: function(text) {
28561         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28562     },
28563     /**
28564      * Resets the internal buffer if one wants to reuse the writer.
28565      *
28566      * @method reset
28567      */
28568     reset: function() {
28569         this.html.length = 0;
28570         this.state = [];
28571         this.pushState({
28572             indentstr : '',
28573             in_pre : false, 
28574             in_inline : false
28575         })
28576     },
28577     /**
28578      * Returns the contents that got serialized.
28579      *
28580      * @method getContent
28581      * @return {String} HTML contents that got written down.
28582      */
28583     getContent: function() {
28584         return this.html.join('').replace(/\n$/, '');
28585     },
28586     
28587     pushState : function(cfg)
28588     {
28589         this.state.push(cfg);
28590         Roo.apply(this, cfg);
28591     },
28592     
28593     popState : function()
28594     {
28595         if (this.state.length < 1) {
28596             return; // nothing to push
28597         }
28598         var cfg = {
28599             in_pre: false,
28600             indentstr : ''
28601         };
28602         this.state.pop();
28603         if (this.state.length > 0) {
28604             cfg = this.state[this.state.length-1]; 
28605         }
28606         Roo.apply(this, cfg);
28607     },
28608     
28609     addLine: function()
28610     {
28611         if (this.html.length < 1) {
28612             return;
28613         }
28614         
28615         
28616         var value = this.html[this.html.length - 1];
28617         if (value.length > 0 && '\n' !== value) {
28618             this.html.push('\n');
28619         }
28620     }
28621     
28622     
28623 //'pre script noscript style textarea video audio iframe object code'
28624 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28625 // inline 
28626 };
28627
28628 Roo.htmleditor.TidyWriter.inline_elements = [
28629         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28630         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28631 ];
28632 Roo.htmleditor.TidyWriter.shortend_elements = [
28633     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28634     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28635 ];
28636
28637 Roo.htmleditor.TidyWriter.whitespace_elements = [
28638     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28639 ];/***
28640  * This is based loosely on tinymce 
28641  * @class Roo.htmleditor.TidyEntities
28642  * @static
28643  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28644  *
28645  * Not 100% sure this is actually used or needed.
28646  */
28647
28648 Roo.htmleditor.TidyEntities = {
28649     
28650     /**
28651      * initialize data..
28652      */
28653     init : function (){
28654      
28655         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28656        
28657     },
28658
28659
28660     buildEntitiesLookup: function(items, radix) {
28661         var i, chr, entity, lookup = {};
28662         if (!items) {
28663             return {};
28664         }
28665         items = typeof(items) == 'string' ? items.split(',') : items;
28666         radix = radix || 10;
28667         // Build entities lookup table
28668         for (i = 0; i < items.length; i += 2) {
28669             chr = String.fromCharCode(parseInt(items[i], radix));
28670             // Only add non base entities
28671             if (!this.baseEntities[chr]) {
28672                 entity = '&' + items[i + 1] + ';';
28673                 lookup[chr] = entity;
28674                 lookup[entity] = chr;
28675             }
28676         }
28677         return lookup;
28678         
28679     },
28680     
28681     asciiMap : {
28682             128: '€',
28683             130: '‚',
28684             131: 'ƒ',
28685             132: '„',
28686             133: '…',
28687             134: '†',
28688             135: '‡',
28689             136: 'ˆ',
28690             137: '‰',
28691             138: 'Š',
28692             139: '‹',
28693             140: 'Œ',
28694             142: 'Ž',
28695             145: '‘',
28696             146: '’',
28697             147: '“',
28698             148: '”',
28699             149: '•',
28700             150: '–',
28701             151: '—',
28702             152: '˜',
28703             153: '™',
28704             154: 'š',
28705             155: '›',
28706             156: 'œ',
28707             158: 'ž',
28708             159: 'Ÿ'
28709     },
28710     // Raw entities
28711     baseEntities : {
28712         '"': '&quot;',
28713         // Needs to be escaped since the YUI compressor would otherwise break the code
28714         '\'': '&#39;',
28715         '<': '&lt;',
28716         '>': '&gt;',
28717         '&': '&amp;',
28718         '`': '&#96;'
28719     },
28720     // Reverse lookup table for raw entities
28721     reverseEntities : {
28722         '&lt;': '<',
28723         '&gt;': '>',
28724         '&amp;': '&',
28725         '&quot;': '"',
28726         '&apos;': '\''
28727     },
28728     
28729     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28730     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28731     rawCharsRegExp : /[<>&\"\']/g,
28732     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28733     namedEntities  : false,
28734     namedEntitiesData : [ 
28735         '50',
28736         'nbsp',
28737         '51',
28738         'iexcl',
28739         '52',
28740         'cent',
28741         '53',
28742         'pound',
28743         '54',
28744         'curren',
28745         '55',
28746         'yen',
28747         '56',
28748         'brvbar',
28749         '57',
28750         'sect',
28751         '58',
28752         'uml',
28753         '59',
28754         'copy',
28755         '5a',
28756         'ordf',
28757         '5b',
28758         'laquo',
28759         '5c',
28760         'not',
28761         '5d',
28762         'shy',
28763         '5e',
28764         'reg',
28765         '5f',
28766         'macr',
28767         '5g',
28768         'deg',
28769         '5h',
28770         'plusmn',
28771         '5i',
28772         'sup2',
28773         '5j',
28774         'sup3',
28775         '5k',
28776         'acute',
28777         '5l',
28778         'micro',
28779         '5m',
28780         'para',
28781         '5n',
28782         'middot',
28783         '5o',
28784         'cedil',
28785         '5p',
28786         'sup1',
28787         '5q',
28788         'ordm',
28789         '5r',
28790         'raquo',
28791         '5s',
28792         'frac14',
28793         '5t',
28794         'frac12',
28795         '5u',
28796         'frac34',
28797         '5v',
28798         'iquest',
28799         '60',
28800         'Agrave',
28801         '61',
28802         'Aacute',
28803         '62',
28804         'Acirc',
28805         '63',
28806         'Atilde',
28807         '64',
28808         'Auml',
28809         '65',
28810         'Aring',
28811         '66',
28812         'AElig',
28813         '67',
28814         'Ccedil',
28815         '68',
28816         'Egrave',
28817         '69',
28818         'Eacute',
28819         '6a',
28820         'Ecirc',
28821         '6b',
28822         'Euml',
28823         '6c',
28824         'Igrave',
28825         '6d',
28826         'Iacute',
28827         '6e',
28828         'Icirc',
28829         '6f',
28830         'Iuml',
28831         '6g',
28832         'ETH',
28833         '6h',
28834         'Ntilde',
28835         '6i',
28836         'Ograve',
28837         '6j',
28838         'Oacute',
28839         '6k',
28840         'Ocirc',
28841         '6l',
28842         'Otilde',
28843         '6m',
28844         'Ouml',
28845         '6n',
28846         'times',
28847         '6o',
28848         'Oslash',
28849         '6p',
28850         'Ugrave',
28851         '6q',
28852         'Uacute',
28853         '6r',
28854         'Ucirc',
28855         '6s',
28856         'Uuml',
28857         '6t',
28858         'Yacute',
28859         '6u',
28860         'THORN',
28861         '6v',
28862         'szlig',
28863         '70',
28864         'agrave',
28865         '71',
28866         'aacute',
28867         '72',
28868         'acirc',
28869         '73',
28870         'atilde',
28871         '74',
28872         'auml',
28873         '75',
28874         'aring',
28875         '76',
28876         'aelig',
28877         '77',
28878         'ccedil',
28879         '78',
28880         'egrave',
28881         '79',
28882         'eacute',
28883         '7a',
28884         'ecirc',
28885         '7b',
28886         'euml',
28887         '7c',
28888         'igrave',
28889         '7d',
28890         'iacute',
28891         '7e',
28892         'icirc',
28893         '7f',
28894         'iuml',
28895         '7g',
28896         'eth',
28897         '7h',
28898         'ntilde',
28899         '7i',
28900         'ograve',
28901         '7j',
28902         'oacute',
28903         '7k',
28904         'ocirc',
28905         '7l',
28906         'otilde',
28907         '7m',
28908         'ouml',
28909         '7n',
28910         'divide',
28911         '7o',
28912         'oslash',
28913         '7p',
28914         'ugrave',
28915         '7q',
28916         'uacute',
28917         '7r',
28918         'ucirc',
28919         '7s',
28920         'uuml',
28921         '7t',
28922         'yacute',
28923         '7u',
28924         'thorn',
28925         '7v',
28926         'yuml',
28927         'ci',
28928         'fnof',
28929         'sh',
28930         'Alpha',
28931         'si',
28932         'Beta',
28933         'sj',
28934         'Gamma',
28935         'sk',
28936         'Delta',
28937         'sl',
28938         'Epsilon',
28939         'sm',
28940         'Zeta',
28941         'sn',
28942         'Eta',
28943         'so',
28944         'Theta',
28945         'sp',
28946         'Iota',
28947         'sq',
28948         'Kappa',
28949         'sr',
28950         'Lambda',
28951         'ss',
28952         'Mu',
28953         'st',
28954         'Nu',
28955         'su',
28956         'Xi',
28957         'sv',
28958         'Omicron',
28959         't0',
28960         'Pi',
28961         't1',
28962         'Rho',
28963         't3',
28964         'Sigma',
28965         't4',
28966         'Tau',
28967         't5',
28968         'Upsilon',
28969         't6',
28970         'Phi',
28971         't7',
28972         'Chi',
28973         't8',
28974         'Psi',
28975         't9',
28976         'Omega',
28977         'th',
28978         'alpha',
28979         'ti',
28980         'beta',
28981         'tj',
28982         'gamma',
28983         'tk',
28984         'delta',
28985         'tl',
28986         'epsilon',
28987         'tm',
28988         'zeta',
28989         'tn',
28990         'eta',
28991         'to',
28992         'theta',
28993         'tp',
28994         'iota',
28995         'tq',
28996         'kappa',
28997         'tr',
28998         'lambda',
28999         'ts',
29000         'mu',
29001         'tt',
29002         'nu',
29003         'tu',
29004         'xi',
29005         'tv',
29006         'omicron',
29007         'u0',
29008         'pi',
29009         'u1',
29010         'rho',
29011         'u2',
29012         'sigmaf',
29013         'u3',
29014         'sigma',
29015         'u4',
29016         'tau',
29017         'u5',
29018         'upsilon',
29019         'u6',
29020         'phi',
29021         'u7',
29022         'chi',
29023         'u8',
29024         'psi',
29025         'u9',
29026         'omega',
29027         'uh',
29028         'thetasym',
29029         'ui',
29030         'upsih',
29031         'um',
29032         'piv',
29033         '812',
29034         'bull',
29035         '816',
29036         'hellip',
29037         '81i',
29038         'prime',
29039         '81j',
29040         'Prime',
29041         '81u',
29042         'oline',
29043         '824',
29044         'frasl',
29045         '88o',
29046         'weierp',
29047         '88h',
29048         'image',
29049         '88s',
29050         'real',
29051         '892',
29052         'trade',
29053         '89l',
29054         'alefsym',
29055         '8cg',
29056         'larr',
29057         '8ch',
29058         'uarr',
29059         '8ci',
29060         'rarr',
29061         '8cj',
29062         'darr',
29063         '8ck',
29064         'harr',
29065         '8dl',
29066         'crarr',
29067         '8eg',
29068         'lArr',
29069         '8eh',
29070         'uArr',
29071         '8ei',
29072         'rArr',
29073         '8ej',
29074         'dArr',
29075         '8ek',
29076         'hArr',
29077         '8g0',
29078         'forall',
29079         '8g2',
29080         'part',
29081         '8g3',
29082         'exist',
29083         '8g5',
29084         'empty',
29085         '8g7',
29086         'nabla',
29087         '8g8',
29088         'isin',
29089         '8g9',
29090         'notin',
29091         '8gb',
29092         'ni',
29093         '8gf',
29094         'prod',
29095         '8gh',
29096         'sum',
29097         '8gi',
29098         'minus',
29099         '8gn',
29100         'lowast',
29101         '8gq',
29102         'radic',
29103         '8gt',
29104         'prop',
29105         '8gu',
29106         'infin',
29107         '8h0',
29108         'ang',
29109         '8h7',
29110         'and',
29111         '8h8',
29112         'or',
29113         '8h9',
29114         'cap',
29115         '8ha',
29116         'cup',
29117         '8hb',
29118         'int',
29119         '8hk',
29120         'there4',
29121         '8hs',
29122         'sim',
29123         '8i5',
29124         'cong',
29125         '8i8',
29126         'asymp',
29127         '8j0',
29128         'ne',
29129         '8j1',
29130         'equiv',
29131         '8j4',
29132         'le',
29133         '8j5',
29134         'ge',
29135         '8k2',
29136         'sub',
29137         '8k3',
29138         'sup',
29139         '8k4',
29140         'nsub',
29141         '8k6',
29142         'sube',
29143         '8k7',
29144         'supe',
29145         '8kl',
29146         'oplus',
29147         '8kn',
29148         'otimes',
29149         '8l5',
29150         'perp',
29151         '8m5',
29152         'sdot',
29153         '8o8',
29154         'lceil',
29155         '8o9',
29156         'rceil',
29157         '8oa',
29158         'lfloor',
29159         '8ob',
29160         'rfloor',
29161         '8p9',
29162         'lang',
29163         '8pa',
29164         'rang',
29165         '9ea',
29166         'loz',
29167         '9j0',
29168         'spades',
29169         '9j3',
29170         'clubs',
29171         '9j5',
29172         'hearts',
29173         '9j6',
29174         'diams',
29175         'ai',
29176         'OElig',
29177         'aj',
29178         'oelig',
29179         'b0',
29180         'Scaron',
29181         'b1',
29182         'scaron',
29183         'bo',
29184         'Yuml',
29185         'm6',
29186         'circ',
29187         'ms',
29188         'tilde',
29189         '802',
29190         'ensp',
29191         '803',
29192         'emsp',
29193         '809',
29194         'thinsp',
29195         '80c',
29196         'zwnj',
29197         '80d',
29198         'zwj',
29199         '80e',
29200         'lrm',
29201         '80f',
29202         'rlm',
29203         '80j',
29204         'ndash',
29205         '80k',
29206         'mdash',
29207         '80o',
29208         'lsquo',
29209         '80p',
29210         'rsquo',
29211         '80q',
29212         'sbquo',
29213         '80s',
29214         'ldquo',
29215         '80t',
29216         'rdquo',
29217         '80u',
29218         'bdquo',
29219         '810',
29220         'dagger',
29221         '811',
29222         'Dagger',
29223         '81g',
29224         'permil',
29225         '81p',
29226         'lsaquo',
29227         '81q',
29228         'rsaquo',
29229         '85c',
29230         'euro'
29231     ],
29232
29233          
29234     /**
29235      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
29236      *
29237      * @method encodeRaw
29238      * @param {String} text Text to encode.
29239      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
29240      * @return {String} Entity encoded text.
29241      */
29242     encodeRaw: function(text, attr)
29243     {
29244         var t = this;
29245         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
29246             return t.baseEntities[chr] || chr;
29247         });
29248     },
29249     /**
29250      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
29251      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
29252      * and is exposed as the DOMUtils.encode function.
29253      *
29254      * @method encodeAllRaw
29255      * @param {String} text Text to encode.
29256      * @return {String} Entity encoded text.
29257      */
29258     encodeAllRaw: function(text) {
29259         var t = this;
29260         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
29261             return t.baseEntities[chr] || chr;
29262         });
29263     },
29264     /**
29265      * Encodes the specified string using numeric entities. The core entities will be
29266      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
29267      *
29268      * @method encodeNumeric
29269      * @param {String} text Text to encode.
29270      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
29271      * @return {String} Entity encoded text.
29272      */
29273     encodeNumeric: function(text, attr) {
29274         var t = this;
29275         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
29276             // Multi byte sequence convert it to a single entity
29277             if (chr.length > 1) {
29278                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
29279             }
29280             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
29281         });
29282     },
29283     /**
29284      * Encodes the specified string using named entities. The core entities will be encoded
29285      * as named ones but all non lower ascii characters will be encoded into named entities.
29286      *
29287      * @method encodeNamed
29288      * @param {String} text Text to encode.
29289      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
29290      * @param {Object} entities Optional parameter with entities to use.
29291      * @return {String} Entity encoded text.
29292      */
29293     encodeNamed: function(text, attr, entities) {
29294         var t = this;
29295         entities = entities || this.namedEntities;
29296         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
29297             return t.baseEntities[chr] || entities[chr] || chr;
29298         });
29299     },
29300     /**
29301      * Returns an encode function based on the name(s) and it's optional entities.
29302      *
29303      * @method getEncodeFunc
29304      * @param {String} name Comma separated list of encoders for example named,numeric.
29305      * @param {String} entities Optional parameter with entities to use instead of the built in set.
29306      * @return {function} Encode function to be used.
29307      */
29308     getEncodeFunc: function(name, entities) {
29309         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
29310         var t = this;
29311         function encodeNamedAndNumeric(text, attr) {
29312             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
29313                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
29314             });
29315         }
29316
29317         function encodeCustomNamed(text, attr) {
29318             return t.encodeNamed(text, attr, entities);
29319         }
29320         // Replace + with , to be compatible with previous TinyMCE versions
29321         name = this.makeMap(name.replace(/\+/g, ','));
29322         // Named and numeric encoder
29323         if (name.named && name.numeric) {
29324             return this.encodeNamedAndNumeric;
29325         }
29326         // Named encoder
29327         if (name.named) {
29328             // Custom names
29329             if (entities) {
29330                 return encodeCustomNamed;
29331             }
29332             return this.encodeNamed;
29333         }
29334         // Numeric
29335         if (name.numeric) {
29336             return this.encodeNumeric;
29337         }
29338         // Raw encoder
29339         return this.encodeRaw;
29340     },
29341     /**
29342      * Decodes the specified string, this will replace entities with raw UTF characters.
29343      *
29344      * @method decode
29345      * @param {String} text Text to entity decode.
29346      * @return {String} Entity decoded string.
29347      */
29348     decode: function(text)
29349     {
29350         var  t = this;
29351         return text.replace(this.entityRegExp, function(all, numeric) {
29352             if (numeric) {
29353                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
29354                 // Support upper UTF
29355                 if (numeric > 65535) {
29356                     numeric -= 65536;
29357                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
29358                 }
29359                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
29360             }
29361             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
29362         });
29363     },
29364     nativeDecode : function (text) {
29365         return text;
29366     },
29367     makeMap : function (items, delim, map) {
29368                 var i;
29369                 items = items || [];
29370                 delim = delim || ',';
29371                 if (typeof items == "string") {
29372                         items = items.split(delim);
29373                 }
29374                 map = map || {};
29375                 i = items.length;
29376                 while (i--) {
29377                         map[items[i]] = {};
29378                 }
29379                 return map;
29380         }
29381 };
29382     
29383     
29384     
29385 Roo.htmleditor.TidyEntities.init();
29386 /**
29387  * @class Roo.htmleditor.KeyEnter
29388  * Handle Enter press..
29389  * @cfg {Roo.HtmlEditorCore} core the editor.
29390  * @constructor
29391  * Create a new Filter.
29392  * @param {Object} config Configuration options
29393  */
29394
29395
29396
29397
29398
29399 Roo.htmleditor.KeyEnter = function(cfg) {
29400     Roo.apply(this, cfg);
29401     // this does not actually call walk as it's really just a abstract class
29402  
29403     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
29404 }
29405
29406 //Roo.htmleditor.KeyEnter.i = 0;
29407
29408
29409 Roo.htmleditor.KeyEnter.prototype = {
29410     
29411     core : false,
29412     
29413     keypress : function(e)
29414     {
29415         if (e.charCode != 13 && e.charCode != 10) {
29416             Roo.log([e.charCode,e]);
29417             return true;
29418         }
29419         e.preventDefault();
29420         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
29421         var doc = this.core.doc;
29422           //add a new line
29423        
29424     
29425         var sel = this.core.getSelection();
29426         var range = sel.getRangeAt(0);
29427         var n = range.commonAncestorContainer;
29428         var pc = range.closest([ 'ol', 'ul']);
29429         var pli = range.closest('li');
29430         if (!pc || e.ctrlKey) {
29431             // on it list, or ctrl pressed.
29432             if (!e.ctrlKey) {
29433                 sel.insertNode('br', 'after'); 
29434             } else {
29435                 // only do this if we have ctrl key..
29436                 var br = doc.createElement('br');
29437                 br.className = 'clear';
29438                 br.setAttribute('style', 'clear: both');
29439                 sel.insertNode(br, 'after'); 
29440             }
29441             
29442          
29443             this.core.undoManager.addEvent();
29444             this.core.fireEditorEvent(e);
29445             return false;
29446         }
29447         
29448         // deal with <li> insetion
29449         if (pli.innerText.trim() == '' &&
29450             pli.previousSibling &&
29451             pli.previousSibling.nodeName == 'LI' &&
29452             pli.previousSibling.innerText.trim() ==  '') {
29453             pli.parentNode.removeChild(pli.previousSibling);
29454             sel.cursorAfter(pc);
29455             this.core.undoManager.addEvent();
29456             this.core.fireEditorEvent(e);
29457             return false;
29458         }
29459     
29460         var li = doc.createElement('LI');
29461         li.innerHTML = '&nbsp;';
29462         if (!pli || !pli.firstSibling) {
29463             pc.appendChild(li);
29464         } else {
29465             pli.parentNode.insertBefore(li, pli.firstSibling);
29466         }
29467         sel.cursorText (li.firstChild);
29468       
29469         this.core.undoManager.addEvent();
29470         this.core.fireEditorEvent(e);
29471
29472         return false;
29473         
29474     
29475         
29476         
29477          
29478     }
29479 };
29480      
29481 /**
29482  * @class Roo.htmleditor.Block
29483  * Base class for html editor blocks - do not use it directly .. extend it..
29484  * @cfg {DomElement} node The node to apply stuff to.
29485  * @cfg {String} friendly_name the name that appears in the context bar about this block
29486  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
29487  
29488  * @constructor
29489  * Create a new Filter.
29490  * @param {Object} config Configuration options
29491  */
29492
29493 Roo.htmleditor.Block  = function(cfg)
29494 {
29495     // do nothing .. should not be called really.
29496 }
29497 /**
29498  * factory method to get the block from an element (using cache if necessary)
29499  * @static
29500  * @param {HtmlElement} the dom element
29501  */
29502 Roo.htmleditor.Block.factory = function(node)
29503 {
29504     var cc = Roo.htmleditor.Block.cache;
29505     var id = Roo.get(node).id;
29506     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
29507         Roo.htmleditor.Block.cache[id].readElement(node);
29508         return Roo.htmleditor.Block.cache[id];
29509     }
29510     var db  = node.getAttribute('data-block');
29511     if (!db) {
29512         db = node.nodeName.toLowerCase().toUpperCaseFirst();
29513     }
29514     var cls = Roo.htmleditor['Block' + db];
29515     if (typeof(cls) == 'undefined') {
29516         //Roo.log(node.getAttribute('data-block'));
29517         Roo.log("OOps missing block : " + 'Block' + db);
29518         return false;
29519     }
29520     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29521     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
29522 };
29523
29524 /**
29525  * initalize all Elements from content that are 'blockable'
29526  * @static
29527  * @param the body element
29528  */
29529 Roo.htmleditor.Block.initAll = function(body, type)
29530 {
29531     if (typeof(type) == 'undefined') {
29532         var ia = Roo.htmleditor.Block.initAll;
29533         ia(body,'table');
29534         ia(body,'td');
29535         ia(body,'figure');
29536         return;
29537     }
29538     Roo.each(Roo.get(body).query(type), function(e) {
29539         Roo.htmleditor.Block.factory(e);    
29540     },this);
29541 };
29542 // question goes here... do we need to clear out this cache sometimes?
29543 // or show we make it relivant to the htmleditor.
29544 Roo.htmleditor.Block.cache = {};
29545
29546 Roo.htmleditor.Block.prototype = {
29547     
29548     node : false,
29549     
29550      // used by context menu
29551     friendly_name : 'Based Block',
29552     
29553     // text for button to delete this element
29554     deleteTitle : false,
29555     
29556     context : false,
29557     /**
29558      * Update a node with values from this object
29559      * @param {DomElement} node
29560      */
29561     updateElement : function(node)
29562     {
29563         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29564     },
29565      /**
29566      * convert to plain HTML for calling insertAtCursor..
29567      */
29568     toHTML : function()
29569     {
29570         return Roo.DomHelper.markup(this.toObject());
29571     },
29572     /**
29573      * used by readEleemnt to extract data from a node
29574      * may need improving as it's pretty basic
29575      
29576      * @param {DomElement} node
29577      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29578      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29579      * @param {String} style the style property - eg. text-align
29580      */
29581     getVal : function(node, tag, attr, style)
29582     {
29583         var n = node;
29584         if (tag !== true && n.tagName != tag.toUpperCase()) {
29585             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29586             // but kiss for now.
29587             n = node.getElementsByTagName(tag).item(0);
29588         }
29589         if (!n) {
29590             return '';
29591         }
29592         if (attr === false) {
29593             return n;
29594         }
29595         if (attr == 'html') {
29596             return n.innerHTML;
29597         }
29598         if (attr == 'style') {
29599             return n.style[style]; 
29600         }
29601         
29602         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29603             
29604     },
29605     /**
29606      * create a DomHelper friendly object - for use with 
29607      * Roo.DomHelper.markup / overwrite / etc..
29608      * (override this)
29609      */
29610     toObject : function()
29611     {
29612         return {};
29613     },
29614       /**
29615      * Read a node that has a 'data-block' property - and extract the values from it.
29616      * @param {DomElement} node - the node
29617      */
29618     readElement : function(node)
29619     {
29620         
29621     } 
29622     
29623     
29624 };
29625
29626  
29627
29628 /**
29629  * @class Roo.htmleditor.BlockFigure
29630  * Block that has an image and a figcaption
29631  * @cfg {String} image_src the url for the image
29632  * @cfg {String} align (left|right) alignment for the block default left
29633  * @cfg {String} caption the text to appear below  (and in the alt tag)
29634  * @cfg {String} caption_display (block|none) display or not the caption
29635  * @cfg {String|number} image_width the width of the image number or %?
29636  * @cfg {String|number} image_height the height of the image number or %?
29637  * 
29638  * @constructor
29639  * Create a new Filter.
29640  * @param {Object} config Configuration options
29641  */
29642
29643 Roo.htmleditor.BlockFigure = function(cfg)
29644 {
29645     if (cfg.node) {
29646         this.readElement(cfg.node);
29647         this.updateElement(cfg.node);
29648     }
29649     Roo.apply(this, cfg);
29650 }
29651 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29652  
29653     
29654     // setable values.
29655     image_src: '',
29656     align: 'center',
29657     caption : '',
29658     caption_display : 'block',
29659     width : '100%',
29660     cls : '',
29661     href: '',
29662     video_url : '',
29663     
29664     // margin: '2%', not used
29665     
29666     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29667
29668     
29669     // used by context menu
29670     friendly_name : 'Image with caption',
29671     deleteTitle : "Delete Image and Caption",
29672     
29673     contextMenu : function(toolbar)
29674     {
29675         
29676         var block = function() {
29677             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29678         };
29679         
29680         
29681         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29682         
29683         var syncValue = toolbar.editorcore.syncValue;
29684         
29685         var fields = {};
29686         
29687         return [
29688              {
29689                 xtype : 'TextItem',
29690                 text : "Source: ",
29691                 xns : rooui.Toolbar  //Boostrap?
29692             },
29693             {
29694                 xtype : 'Button',
29695                 text: 'Change Image URL',
29696                  
29697                 listeners : {
29698                     click: function (btn, state)
29699                     {
29700                         var b = block();
29701                         
29702                         Roo.MessageBox.show({
29703                             title : "Image Source URL",
29704                             msg : "Enter the url for the image",
29705                             buttons: Roo.MessageBox.OKCANCEL,
29706                             fn: function(btn, val){
29707                                 if (btn != 'ok') {
29708                                     return;
29709                                 }
29710                                 b.image_src = val;
29711                                 b.updateElement();
29712                                 syncValue();
29713                                 toolbar.editorcore.onEditorEvent();
29714                             },
29715                             minWidth:250,
29716                             prompt:true,
29717                             //multiline: multiline,
29718                             modal : true,
29719                             value : b.image_src
29720                         });
29721                     }
29722                 },
29723                 xns : rooui.Toolbar
29724             },
29725          
29726             {
29727                 xtype : 'Button',
29728                 text: 'Change Link URL',
29729                  
29730                 listeners : {
29731                     click: function (btn, state)
29732                     {
29733                         var b = block();
29734                         
29735                         Roo.MessageBox.show({
29736                             title : "Link URL",
29737                             msg : "Enter the url for the link - leave blank to have no link",
29738                             buttons: Roo.MessageBox.OKCANCEL,
29739                             fn: function(btn, val){
29740                                 if (btn != 'ok') {
29741                                     return;
29742                                 }
29743                                 b.href = val;
29744                                 b.updateElement();
29745                                 syncValue();
29746                                 toolbar.editorcore.onEditorEvent();
29747                             },
29748                             minWidth:250,
29749                             prompt:true,
29750                             //multiline: multiline,
29751                             modal : true,
29752                             value : b.href
29753                         });
29754                     }
29755                 },
29756                 xns : rooui.Toolbar
29757             },
29758             {
29759                 xtype : 'Button',
29760                 text: 'Show Video URL',
29761                  
29762                 listeners : {
29763                     click: function (btn, state)
29764                     {
29765                         Roo.MessageBox.alert("Video URL",
29766                             block().video_url == '' ? 'This image is not linked ot a video' :
29767                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29768                     }
29769                 },
29770                 xns : rooui.Toolbar
29771             },
29772             
29773             
29774             {
29775                 xtype : 'TextItem',
29776                 text : "Width: ",
29777                 xns : rooui.Toolbar  //Boostrap?
29778             },
29779             {
29780                 xtype : 'ComboBox',
29781                 allowBlank : false,
29782                 displayField : 'val',
29783                 editable : true,
29784                 listWidth : 100,
29785                 triggerAction : 'all',
29786                 typeAhead : true,
29787                 valueField : 'val',
29788                 width : 70,
29789                 name : 'width',
29790                 listeners : {
29791                     select : function (combo, r, index)
29792                     {
29793                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29794                         var b = block();
29795                         b.width = r.get('val');
29796                         b.updateElement();
29797                         syncValue();
29798                         toolbar.editorcore.onEditorEvent();
29799                     }
29800                 },
29801                 xns : rooui.form,
29802                 store : {
29803                     xtype : 'SimpleStore',
29804                     data : [
29805                         ['100%'],
29806                         ['80%'],
29807                         ['50%'],
29808                         ['20%'],
29809                         ['10%']
29810                     ],
29811                     fields : [ 'val'],
29812                     xns : Roo.data
29813                 }
29814             },
29815             {
29816                 xtype : 'TextItem',
29817                 text : "Align: ",
29818                 xns : rooui.Toolbar  //Boostrap?
29819             },
29820             {
29821                 xtype : 'ComboBox',
29822                 allowBlank : false,
29823                 displayField : 'val',
29824                 editable : true,
29825                 listWidth : 100,
29826                 triggerAction : 'all',
29827                 typeAhead : true,
29828                 valueField : 'val',
29829                 width : 70,
29830                 name : 'align',
29831                 listeners : {
29832                     select : function (combo, r, index)
29833                     {
29834                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29835                         var b = block();
29836                         b.align = r.get('val');
29837                         b.updateElement();
29838                         syncValue();
29839                         toolbar.editorcore.onEditorEvent();
29840                     }
29841                 },
29842                 xns : rooui.form,
29843                 store : {
29844                     xtype : 'SimpleStore',
29845                     data : [
29846                         ['left'],
29847                         ['right'],
29848                         ['center']
29849                     ],
29850                     fields : [ 'val'],
29851                     xns : Roo.data
29852                 }
29853             },
29854             
29855               
29856             {
29857                 xtype : 'Button',
29858                 text: 'Hide Caption',
29859                 name : 'caption_display',
29860                 pressed : false,
29861                 enableToggle : true,
29862                 setValue : function(v) {
29863                     // this trigger toggle.
29864                      
29865                     this.setText(v ? "Hide Caption" : "Show Caption");
29866                     this.setPressed(v != 'block');
29867                 },
29868                 listeners : {
29869                     toggle: function (btn, state)
29870                     {
29871                         var b  = block();
29872                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29873                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29874                         b.updateElement();
29875                         syncValue();
29876                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29877                         toolbar.editorcore.onEditorEvent();
29878                     }
29879                 },
29880                 xns : rooui.Toolbar
29881             }
29882         ];
29883         
29884     },
29885     /**
29886      * create a DomHelper friendly object - for use with
29887      * Roo.DomHelper.markup / overwrite / etc..
29888      */
29889     toObject : function()
29890     {
29891         var d = document.createElement('div');
29892         d.innerHTML = this.caption;
29893         
29894         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29895         
29896         var iw = this.align == 'center' ? this.width : '100%';
29897         var img =   {
29898             tag : 'img',
29899             contenteditable : 'false',
29900             src : this.image_src,
29901             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29902             style: {
29903                 width : iw,
29904                 maxWidth : iw + ' !important', // this is not getting rendered?
29905                 margin : m  
29906                 
29907             }
29908         };
29909         /*
29910         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29911                     '<a href="{2}">' + 
29912                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29913                     '</a>' + 
29914                 '</div>',
29915         */
29916                 
29917         if (this.href.length > 0) {
29918             img = {
29919                 tag : 'a',
29920                 href: this.href,
29921                 contenteditable : 'true',
29922                 cn : [
29923                     img
29924                 ]
29925             };
29926         }
29927         
29928         
29929         if (this.video_url.length > 0) {
29930             img = {
29931                 tag : 'div',
29932                 cls : this.cls,
29933                 frameborder : 0,
29934                 allowfullscreen : true,
29935                 width : 420,  // these are for video tricks - that we replace the outer
29936                 height : 315,
29937                 src : this.video_url,
29938                 cn : [
29939                     img
29940                 ]
29941             };
29942         }
29943
29944
29945   
29946         var ret =   {
29947             tag: 'figure',
29948             'data-block' : 'Figure',
29949             'data-width' : this.width,
29950             'data-caption' : this.caption, 
29951             'data-caption-display' : this.caption_display,
29952             contenteditable : 'false',
29953             
29954             style : {
29955                 display: 'block',
29956                 float :  this.align ,
29957                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29958                 width : this.align == 'center' ? '100%' : this.width,
29959                 margin:  '0px',
29960                 padding: this.align == 'center' ? '0' : '0 10px' ,
29961                 textAlign : this.align   // seems to work for email..
29962                 
29963             },
29964             
29965             align : this.align,
29966             cn : [
29967                 img
29968             ]
29969         };
29970
29971         // show figcaption only if caption_display is 'block'
29972         if(this.caption_display == 'block') {
29973             ret['cn'].push({
29974                 tag: 'figcaption',
29975                 style : {
29976                     textAlign : 'left',
29977                     fontSize : '16px',
29978                     lineHeight : '24px',
29979                     display : this.caption_display,
29980                     maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29981                     margin: m,
29982                     width: this.align == 'center' ?  this.width : '100%' 
29983                 
29984                      
29985                 },
29986                 cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29987                 cn : [
29988                     {
29989                         tag: 'div',
29990                         style  : {
29991                             marginTop : '16px',
29992                             textAlign : 'start'
29993                         },
29994                         align: 'left',
29995                         cn : [
29996                             {
29997                                 // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29998                                 tag : 'i',
29999                                 contenteditable : Roo.htmleditor.BlockFigure.caption_edit,
30000                                 html : this.caption.length ? this.caption : "Caption" // fake caption
30001                             }
30002                             
30003                         ]
30004                     }
30005                     
30006                 ]
30007                 
30008             });
30009         }
30010         return ret;
30011          
30012     },
30013     
30014     readElement : function(node)
30015     {
30016         // this should not really come from the link...
30017         this.video_url = this.getVal(node, 'div', 'src');
30018         this.cls = this.getVal(node, 'div', 'class');
30019         this.href = this.getVal(node, 'a', 'href');
30020         
30021         
30022         this.image_src = this.getVal(node, 'img', 'src');
30023          
30024         this.align = this.getVal(node, 'figure', 'align');
30025
30026         // caption display is stored in figure
30027         this.caption_display = this.getVal(node, true, 'data-caption-display');
30028
30029         // backward compatible
30030         // it was stored in figcaption
30031         if(this.caption_display == '') {
30032             this.caption_display = this.getVal(node, 'figcaption', 'data-display');
30033         }
30034
30035         // read caption from figcaption
30036         var figcaption = this.getVal(node, 'figcaption', false);
30037
30038         if (figcaption !== '') {
30039             this.caption = this.getVal(figcaption, 'i', 'html');
30040         }
30041                 
30042
30043         // read caption from data-caption in figure if no caption from figcaption
30044         var dc = this.getVal(node, true, 'data-caption');
30045
30046         if(this.caption_display == 'none' && dc && dc.length){
30047             this.caption = dc;
30048         }
30049
30050         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
30051         this.width = this.getVal(node, true, 'data-width');
30052         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
30053         
30054     },
30055     removeNode : function()
30056     {
30057         return this.node;
30058     }
30059     
30060   
30061    
30062      
30063     
30064     
30065     
30066     
30067 });
30068
30069 Roo.apply(Roo.htmleditor.BlockFigure, {
30070     caption_edit : true
30071 });
30072
30073  
30074
30075 /**
30076  * @class Roo.htmleditor.BlockTable
30077  * Block that manages a table
30078  * 
30079  * @constructor
30080  * Create a new Filter.
30081  * @param {Object} config Configuration options
30082  */
30083
30084 Roo.htmleditor.BlockTable = function(cfg)
30085 {
30086     if (cfg.node) {
30087         this.readElement(cfg.node);
30088         this.updateElement(cfg.node);
30089     }
30090     Roo.apply(this, cfg);
30091     if (!cfg.node) {
30092         this.rows = [];
30093         for(var r = 0; r < this.no_row; r++) {
30094             this.rows[r] = [];
30095             for(var c = 0; c < this.no_col; c++) {
30096                 this.rows[r][c] = this.emptyCell();
30097             }
30098         }
30099     }
30100     
30101     
30102 }
30103 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
30104  
30105     rows : false,
30106     no_col : 1,
30107     no_row : 1,
30108     
30109     
30110     width: '100%',
30111     
30112     // used by context menu
30113     friendly_name : 'Table',
30114     deleteTitle : 'Delete Table',
30115     // context menu is drawn once..
30116     
30117     contextMenu : function(toolbar)
30118     {
30119         
30120         var block = function() {
30121             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30122         };
30123         
30124         
30125         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30126         
30127         var syncValue = toolbar.editorcore.syncValue;
30128         
30129         var fields = {};
30130         
30131         return [
30132             {
30133                 xtype : 'TextItem',
30134                 text : "Width: ",
30135                 xns : rooui.Toolbar  //Boostrap?
30136             },
30137             {
30138                 xtype : 'ComboBox',
30139                 allowBlank : false,
30140                 displayField : 'val',
30141                 editable : true,
30142                 listWidth : 100,
30143                 triggerAction : 'all',
30144                 typeAhead : true,
30145                 valueField : 'val',
30146                 width : 100,
30147                 name : 'width',
30148                 listeners : {
30149                     select : function (combo, r, index)
30150                     {
30151                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30152                         var b = block();
30153                         b.width = r.get('val');
30154                         b.updateElement();
30155                         syncValue();
30156                         toolbar.editorcore.onEditorEvent();
30157                     }
30158                 },
30159                 xns : rooui.form,
30160                 store : {
30161                     xtype : 'SimpleStore',
30162                     data : [
30163                         ['100%'],
30164                         ['auto']
30165                     ],
30166                     fields : [ 'val'],
30167                     xns : Roo.data
30168                 }
30169             },
30170             // -------- Cols
30171             
30172             {
30173                 xtype : 'TextItem',
30174                 text : "Columns: ",
30175                 xns : rooui.Toolbar  //Boostrap?
30176             },
30177          
30178             {
30179                 xtype : 'Button',
30180                 text: '-',
30181                 listeners : {
30182                     click : function (_self, e)
30183                     {
30184                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30185                         block().removeColumn();
30186                         syncValue();
30187                         toolbar.editorcore.onEditorEvent();
30188                     }
30189                 },
30190                 xns : rooui.Toolbar
30191             },
30192             {
30193                 xtype : 'Button',
30194                 text: '+',
30195                 listeners : {
30196                     click : function (_self, e)
30197                     {
30198                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30199                         block().addColumn();
30200                         syncValue();
30201                         toolbar.editorcore.onEditorEvent();
30202                     }
30203                 },
30204                 xns : rooui.Toolbar
30205             },
30206             // -------- ROWS
30207             {
30208                 xtype : 'TextItem',
30209                 text : "Rows: ",
30210                 xns : rooui.Toolbar  //Boostrap?
30211             },
30212          
30213             {
30214                 xtype : 'Button',
30215                 text: '-',
30216                 listeners : {
30217                     click : function (_self, e)
30218                     {
30219                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30220                         block().removeRow();
30221                         syncValue();
30222                         toolbar.editorcore.onEditorEvent();
30223                     }
30224                 },
30225                 xns : rooui.Toolbar
30226             },
30227             {
30228                 xtype : 'Button',
30229                 text: '+',
30230                 listeners : {
30231                     click : function (_self, e)
30232                     {
30233                         block().addRow();
30234                         syncValue();
30235                         toolbar.editorcore.onEditorEvent();
30236                     }
30237                 },
30238                 xns : rooui.Toolbar
30239             },
30240             // -------- ROWS
30241             {
30242                 xtype : 'Button',
30243                 text: 'Reset Column Widths',
30244                 listeners : {
30245                     
30246                     click : function (_self, e)
30247                     {
30248                         block().resetWidths();
30249                         syncValue();
30250                         toolbar.editorcore.onEditorEvent();
30251                     }
30252                 },
30253                 xns : rooui.Toolbar
30254             } 
30255             
30256             
30257             
30258         ];
30259         
30260     },
30261     
30262     
30263   /**
30264      * create a DomHelper friendly object - for use with
30265      * Roo.DomHelper.markup / overwrite / etc..
30266      * ?? should it be called with option to hide all editing features?
30267      */
30268     toObject : function()
30269     {
30270         
30271         var ret = {
30272             tag : 'table',
30273             contenteditable : 'false', // this stops cell selection from picking the table.
30274             'data-block' : 'Table',
30275             style : {
30276                 width:  this.width,
30277                 border : 'solid 1px #000', // ??? hard coded?
30278                 'border-collapse' : 'collapse' 
30279             },
30280             cn : [
30281                 { tag : 'tbody' , cn : [] }
30282             ]
30283         };
30284         
30285         // do we have a head = not really 
30286         var ncols = 0;
30287         Roo.each(this.rows, function( row ) {
30288             var tr = {
30289                 tag: 'tr',
30290                 style : {
30291                     margin: '6px',
30292                     border : 'solid 1px #000',
30293                     textAlign : 'left' 
30294                 },
30295                 cn : [ ]
30296             };
30297             
30298             ret.cn[0].cn.push(tr);
30299             // does the row have any properties? ?? height?
30300             var nc = 0;
30301             Roo.each(row, function( cell ) {
30302                 
30303                 var td = {
30304                     tag : 'td',
30305                     contenteditable :  'true',
30306                     'data-block' : 'Td',
30307                     html : cell.html,
30308                     style : cell.style
30309                 };
30310                 if (cell.colspan > 1) {
30311                     td.colspan = cell.colspan ;
30312                     nc += cell.colspan;
30313                 } else {
30314                     nc++;
30315                 }
30316                 if (cell.rowspan > 1) {
30317                     td.rowspan = cell.rowspan ;
30318                 }
30319                 
30320                 
30321                 // widths ?
30322                 tr.cn.push(td);
30323                     
30324                 
30325             }, this);
30326             ncols = Math.max(nc, ncols);
30327             
30328             
30329         }, this);
30330         // add the header row..
30331         
30332         ncols++;
30333          
30334         
30335         return ret;
30336          
30337     },
30338     
30339     readElement : function(node)
30340     {
30341         node  = node ? node : this.node ;
30342         this.width = this.getVal(node, true, 'style', 'width') || '100%';
30343         
30344         this.rows = [];
30345         this.no_row = 0;
30346         var trs = Array.from(node.rows);
30347         trs.forEach(function(tr) {
30348             var row =  [];
30349             this.rows.push(row);
30350             
30351             this.no_row++;
30352             var no_column = 0;
30353             Array.from(tr.cells).forEach(function(td) {
30354                 
30355                 var add = {
30356                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
30357                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
30358                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
30359                     html : td.innerHTML
30360                 };
30361                 no_column += add.colspan;
30362                      
30363                 
30364                 row.push(add);
30365                 
30366                 
30367             },this);
30368             this.no_col = Math.max(this.no_col, no_column);
30369             
30370             
30371         },this);
30372         
30373         
30374     },
30375     normalizeRows: function()
30376     {
30377         var ret= [];
30378         var rid = -1;
30379         this.rows.forEach(function(row) {
30380             rid++;
30381             ret[rid] = [];
30382             row = this.normalizeRow(row);
30383             var cid = 0;
30384             row.forEach(function(c) {
30385                 while (typeof(ret[rid][cid]) != 'undefined') {
30386                     cid++;
30387                 }
30388                 if (typeof(ret[rid]) == 'undefined') {
30389                     ret[rid] = [];
30390                 }
30391                 ret[rid][cid] = c;
30392                 c.row = rid;
30393                 c.col = cid;
30394                 if (c.rowspan < 2) {
30395                     return;
30396                 }
30397                 
30398                 for(var i = 1 ;i < c.rowspan; i++) {
30399                     if (typeof(ret[rid+i]) == 'undefined') {
30400                         ret[rid+i] = [];
30401                     }
30402                     ret[rid+i][cid] = c;
30403                 }
30404             });
30405         }, this);
30406         return ret;
30407     
30408     },
30409     
30410     normalizeRow: function(row)
30411     {
30412         var ret= [];
30413         row.forEach(function(c) {
30414             if (c.colspan < 2) {
30415                 ret.push(c);
30416                 return;
30417             }
30418             for(var i =0 ;i < c.colspan; i++) {
30419                 ret.push(c);
30420             }
30421         });
30422         return ret;
30423     
30424     },
30425     
30426     deleteColumn : function(sel)
30427     {
30428         if (!sel || sel.type != 'col') {
30429             return;
30430         }
30431         if (this.no_col < 2) {
30432             return;
30433         }
30434         
30435         this.rows.forEach(function(row) {
30436             var cols = this.normalizeRow(row);
30437             var col = cols[sel.col];
30438             if (col.colspan > 1) {
30439                 col.colspan --;
30440             } else {
30441                 row.remove(col);
30442             }
30443             
30444         }, this);
30445         this.no_col--;
30446         
30447     },
30448     removeColumn : function()
30449     {
30450         this.deleteColumn({
30451             type: 'col',
30452             col : this.no_col-1
30453         });
30454         this.updateElement();
30455     },
30456     
30457      
30458     addColumn : function()
30459     {
30460         
30461         this.rows.forEach(function(row) {
30462             row.push(this.emptyCell());
30463            
30464         }, this);
30465         this.updateElement();
30466     },
30467     
30468     deleteRow : function(sel)
30469     {
30470         if (!sel || sel.type != 'row') {
30471             return;
30472         }
30473         
30474         if (this.no_row < 2) {
30475             return;
30476         }
30477         
30478         var rows = this.normalizeRows();
30479         
30480         
30481         rows[sel.row].forEach(function(col) {
30482             if (col.rowspan > 1) {
30483                 col.rowspan--;
30484             } else {
30485                 col.remove = 1; // flage it as removed.
30486             }
30487             
30488         }, this);
30489         var newrows = [];
30490         this.rows.forEach(function(row) {
30491             newrow = [];
30492             row.forEach(function(c) {
30493                 if (typeof(c.remove) == 'undefined') {
30494                     newrow.push(c);
30495                 }
30496                 
30497             });
30498             if (newrow.length > 0) {
30499                 newrows.push(row);
30500             }
30501         });
30502         this.rows =  newrows;
30503         
30504         
30505         
30506         this.no_row--;
30507         this.updateElement();
30508         
30509     },
30510     removeRow : function()
30511     {
30512         this.deleteRow({
30513             type: 'row',
30514             row : this.no_row-1
30515         });
30516         
30517     },
30518     
30519      
30520     addRow : function()
30521     {
30522         
30523         var row = [];
30524         for (var i = 0; i < this.no_col; i++ ) {
30525             
30526             row.push(this.emptyCell());
30527            
30528         }
30529         this.rows.push(row);
30530         this.updateElement();
30531         
30532     },
30533      
30534     // the default cell object... at present...
30535     emptyCell : function() {
30536         return (new Roo.htmleditor.BlockTd({})).toObject();
30537         
30538      
30539     },
30540     
30541     removeNode : function()
30542     {
30543         return this.node;
30544     },
30545     
30546     
30547     
30548     resetWidths : function()
30549     {
30550         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30551             var nn = Roo.htmleditor.Block.factory(n);
30552             nn.width = '';
30553             nn.updateElement(n);
30554         });
30555     }
30556     
30557     
30558     
30559     
30560 })
30561
30562 /**
30563  *
30564  * editing a TD?
30565  *
30566  * since selections really work on the table cell, then editing really should work from there
30567  *
30568  * The original plan was to support merging etc... - but that may not be needed yet..
30569  *
30570  * So this simple version will support:
30571  *   add/remove cols
30572  *   adjust the width +/-
30573  *   reset the width...
30574  *   
30575  *
30576  */
30577
30578
30579  
30580
30581 /**
30582  * @class Roo.htmleditor.BlockTable
30583  * Block that manages a table
30584  * 
30585  * @constructor
30586  * Create a new Filter.
30587  * @param {Object} config Configuration options
30588  */
30589
30590 Roo.htmleditor.BlockTd = function(cfg)
30591 {
30592     if (cfg.node) {
30593         this.readElement(cfg.node);
30594         this.updateElement(cfg.node);
30595     }
30596     Roo.apply(this, cfg);
30597      
30598     
30599     
30600 }
30601 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30602  
30603     node : false,
30604     
30605     width: '',
30606     textAlign : 'left',
30607     valign : 'top',
30608     
30609     colspan : 1,
30610     rowspan : 1,
30611     
30612     
30613     // used by context menu
30614     friendly_name : 'Table Cell',
30615     deleteTitle : false, // use our customer delete
30616     
30617     // context menu is drawn once..
30618     
30619     contextMenu : function(toolbar)
30620     {
30621         
30622         var cell = function() {
30623             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30624         };
30625         
30626         var table = function() {
30627             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30628         };
30629         
30630         var lr = false;
30631         var saveSel = function()
30632         {
30633             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30634         }
30635         var restoreSel = function()
30636         {
30637             if (lr) {
30638                 (function() {
30639                     toolbar.editorcore.focus();
30640                     var cr = toolbar.editorcore.getSelection();
30641                     cr.removeAllRanges();
30642                     cr.addRange(lr);
30643                     toolbar.editorcore.onEditorEvent();
30644                 }).defer(10, this);
30645                 
30646                 
30647             }
30648         }
30649         
30650         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30651         
30652         var syncValue = toolbar.editorcore.syncValue;
30653         
30654         var fields = {};
30655         
30656         return [
30657             {
30658                 xtype : 'Button',
30659                 text : 'Edit Table',
30660                 listeners : {
30661                     click : function() {
30662                         var t = toolbar.tb.selectedNode.closest('table');
30663                         toolbar.editorcore.selectNode(t);
30664                         toolbar.editorcore.onEditorEvent();                        
30665                     }
30666                 }
30667                 
30668             },
30669               
30670            
30671              
30672             {
30673                 xtype : 'TextItem',
30674                 text : "Column Width: ",
30675                  xns : rooui.Toolbar 
30676                
30677             },
30678             {
30679                 xtype : 'Button',
30680                 text: '-',
30681                 listeners : {
30682                     click : function (_self, e)
30683                     {
30684                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30685                         cell().shrinkColumn();
30686                         syncValue();
30687                          toolbar.editorcore.onEditorEvent();
30688                     }
30689                 },
30690                 xns : rooui.Toolbar
30691             },
30692             {
30693                 xtype : 'Button',
30694                 text: '+',
30695                 listeners : {
30696                     click : function (_self, e)
30697                     {
30698                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30699                         cell().growColumn();
30700                         syncValue();
30701                         toolbar.editorcore.onEditorEvent();
30702                     }
30703                 },
30704                 xns : rooui.Toolbar
30705             },
30706             
30707             {
30708                 xtype : 'TextItem',
30709                 text : "Vertical Align: ",
30710                 xns : rooui.Toolbar  //Boostrap?
30711             },
30712             {
30713                 xtype : 'ComboBox',
30714                 allowBlank : false,
30715                 displayField : 'val',
30716                 editable : true,
30717                 listWidth : 100,
30718                 triggerAction : 'all',
30719                 typeAhead : true,
30720                 valueField : 'val',
30721                 width : 100,
30722                 name : 'valign',
30723                 listeners : {
30724                     select : function (combo, r, index)
30725                     {
30726                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30727                         var b = cell();
30728                         b.valign = r.get('val');
30729                         b.updateElement();
30730                         syncValue();
30731                         toolbar.editorcore.onEditorEvent();
30732                     }
30733                 },
30734                 xns : rooui.form,
30735                 store : {
30736                     xtype : 'SimpleStore',
30737                     data : [
30738                         ['top'],
30739                         ['middle'],
30740                         ['bottom'] // there are afew more... 
30741                     ],
30742                     fields : [ 'val'],
30743                     xns : Roo.data
30744                 }
30745             },
30746             
30747             {
30748                 xtype : 'TextItem',
30749                 text : "Merge Cells: ",
30750                  xns : rooui.Toolbar 
30751                
30752             },
30753             
30754             
30755             {
30756                 xtype : 'Button',
30757                 text: 'Right',
30758                 listeners : {
30759                     click : function (_self, e)
30760                     {
30761                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30762                         cell().mergeRight();
30763                         //block().growColumn();
30764                         syncValue();
30765                         toolbar.editorcore.onEditorEvent();
30766                     }
30767                 },
30768                 xns : rooui.Toolbar
30769             },
30770              
30771             {
30772                 xtype : 'Button',
30773                 text: 'Below',
30774                 listeners : {
30775                     click : function (_self, e)
30776                     {
30777                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30778                         cell().mergeBelow();
30779                         //block().growColumn();
30780                         syncValue();
30781                         toolbar.editorcore.onEditorEvent();
30782                     }
30783                 },
30784                 xns : rooui.Toolbar
30785             },
30786             {
30787                 xtype : 'TextItem',
30788                 text : "| ",
30789                  xns : rooui.Toolbar 
30790                
30791             },
30792             
30793             {
30794                 xtype : 'Button',
30795                 text: 'Split',
30796                 listeners : {
30797                     click : function (_self, e)
30798                     {
30799                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30800                         cell().split();
30801                         syncValue();
30802                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30803                         toolbar.editorcore.onEditorEvent();
30804                                              
30805                     }
30806                 },
30807                 xns : rooui.Toolbar
30808             },
30809             {
30810                 xtype : 'Fill',
30811                 xns : rooui.Toolbar 
30812                
30813             },
30814         
30815           
30816             {
30817                 xtype : 'Button',
30818                 text: 'Delete',
30819                  
30820                 xns : rooui.Toolbar,
30821                 menu : {
30822                     xtype : 'Menu',
30823                     xns : rooui.menu,
30824                     items : [
30825                         {
30826                             xtype : 'Item',
30827                             html: 'Column',
30828                             listeners : {
30829                                 click : function (_self, e)
30830                                 {
30831                                     var t = table();
30832                                     
30833                                     cell().deleteColumn();
30834                                     syncValue();
30835                                     toolbar.editorcore.selectNode(t.node);
30836                                     toolbar.editorcore.onEditorEvent();   
30837                                 }
30838                             },
30839                             xns : rooui.menu
30840                         },
30841                         {
30842                             xtype : 'Item',
30843                             html: 'Row',
30844                             listeners : {
30845                                 click : function (_self, e)
30846                                 {
30847                                     var t = table();
30848                                     cell().deleteRow();
30849                                     syncValue();
30850                                     
30851                                     toolbar.editorcore.selectNode(t.node);
30852                                     toolbar.editorcore.onEditorEvent();   
30853                                                          
30854                                 }
30855                             },
30856                             xns : rooui.menu
30857                         },
30858                        {
30859                             xtype : 'Separator',
30860                             xns : rooui.menu
30861                         },
30862                         {
30863                             xtype : 'Item',
30864                             html: 'Table',
30865                             listeners : {
30866                                 click : function (_self, e)
30867                                 {
30868                                     var t = table();
30869                                     var nn = t.node.nextSibling || t.node.previousSibling;
30870                                     t.node.parentNode.removeChild(t.node);
30871                                     if (nn) { 
30872                                         toolbar.editorcore.selectNode(nn, true);
30873                                     }
30874                                     toolbar.editorcore.onEditorEvent();   
30875                                                          
30876                                 }
30877                             },
30878                             xns : rooui.menu
30879                         }
30880                     ]
30881                 }
30882             }
30883             
30884             // align... << fixme
30885             
30886         ];
30887         
30888     },
30889     
30890     
30891   /**
30892      * create a DomHelper friendly object - for use with
30893      * Roo.DomHelper.markup / overwrite / etc..
30894      * ?? should it be called with option to hide all editing features?
30895      */
30896  /**
30897      * create a DomHelper friendly object - for use with
30898      * Roo.DomHelper.markup / overwrite / etc..
30899      * ?? should it be called with option to hide all editing features?
30900      */
30901     toObject : function()
30902     {
30903         var ret = {
30904             tag : 'td',
30905             contenteditable : 'true', // this stops cell selection from picking the table.
30906             'data-block' : 'Td',
30907             valign : this.valign,
30908             style : {  
30909                 'text-align' :  this.textAlign,
30910                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30911                 'border-collapse' : 'collapse',
30912                 padding : '6px', // 8 for desktop / 4 for mobile
30913                 'vertical-align': this.valign
30914             },
30915             html : this.html
30916         };
30917         if (this.width != '') {
30918             ret.width = this.width;
30919             ret.style.width = this.width;
30920         }
30921         
30922         
30923         if (this.colspan > 1) {
30924             ret.colspan = this.colspan ;
30925         } 
30926         if (this.rowspan > 1) {
30927             ret.rowspan = this.rowspan ;
30928         }
30929         
30930            
30931         
30932         return ret;
30933          
30934     },
30935     
30936     readElement : function(node)
30937     {
30938         node  = node ? node : this.node ;
30939         this.width = node.style.width;
30940         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30941         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30942         this.html = node.innerHTML;
30943         if (node.style.textAlign != '') {
30944             this.textAlign = node.style.textAlign;
30945         }
30946         
30947         
30948     },
30949      
30950     // the default cell object... at present...
30951     emptyCell : function() {
30952         return {
30953             colspan :  1,
30954             rowspan :  1,
30955             textAlign : 'left',
30956             html : "&nbsp;" // is this going to be editable now?
30957         };
30958      
30959     },
30960     
30961     removeNode : function()
30962     {
30963         return this.node.closest('table');
30964          
30965     },
30966     
30967     cellData : false,
30968     
30969     colWidths : false,
30970     
30971     toTableArray  : function()
30972     {
30973         var ret = [];
30974         var tab = this.node.closest('tr').closest('table');
30975         Array.from(tab.rows).forEach(function(r, ri){
30976             ret[ri] = [];
30977         });
30978         var rn = 0;
30979         this.colWidths = [];
30980         var all_auto = true;
30981         Array.from(tab.rows).forEach(function(r, ri){
30982             
30983             var cn = 0;
30984             Array.from(r.cells).forEach(function(ce, ci){
30985                 var c =  {
30986                     cell : ce,
30987                     row : rn,
30988                     col: cn,
30989                     colspan : ce.colSpan,
30990                     rowspan : ce.rowSpan
30991                 };
30992                 if (ce.isEqualNode(this.node)) {
30993                     this.cellData = c;
30994                 }
30995                 // if we have been filled up by a row?
30996                 if (typeof(ret[rn][cn]) != 'undefined') {
30997                     while(typeof(ret[rn][cn]) != 'undefined') {
30998                         cn++;
30999                     }
31000                     c.col = cn;
31001                 }
31002                 
31003                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
31004                     this.colWidths[cn] =   ce.style.width;
31005                     if (this.colWidths[cn] != '') {
31006                         all_auto = false;
31007                     }
31008                 }
31009                 
31010                 
31011                 if (c.colspan < 2 && c.rowspan < 2 ) {
31012                     ret[rn][cn] = c;
31013                     cn++;
31014                     return;
31015                 }
31016                 for(var j = 0; j < c.rowspan; j++) {
31017                     if (typeof(ret[rn+j]) == 'undefined') {
31018                         continue; // we have a problem..
31019                     }
31020                     ret[rn+j][cn] = c;
31021                     for(var i = 0; i < c.colspan; i++) {
31022                         ret[rn+j][cn+i] = c;
31023                     }
31024                 }
31025                 
31026                 cn += c.colspan;
31027             }, this);
31028             rn++;
31029         }, this);
31030         
31031         // initalize widths.?
31032         // either all widths or no widths..
31033         if (all_auto) {
31034             this.colWidths[0] = false; // no widths flag.
31035         }
31036         
31037         
31038         return ret;
31039         
31040     },
31041     
31042     
31043     
31044     
31045     mergeRight: function()
31046     {
31047          
31048         // get the contents of the next cell along..
31049         var tr = this.node.closest('tr');
31050         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
31051         if (i >= tr.childNodes.length - 1) {
31052             return; // no cells on right to merge with.
31053         }
31054         var table = this.toTableArray();
31055         
31056         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
31057             return; // nothing right?
31058         }
31059         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
31060         // right cell - must be same rowspan and on the same row.
31061         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
31062             return; // right hand side is not same rowspan.
31063         }
31064         
31065         
31066         
31067         this.node.innerHTML += ' ' + rc.cell.innerHTML;
31068         tr.removeChild(rc.cell);
31069         this.colspan += rc.colspan;
31070         this.node.setAttribute('colspan', this.colspan);
31071
31072         var table = this.toTableArray();
31073         this.normalizeWidths(table);
31074         this.updateWidths(table);
31075     },
31076     
31077     
31078     mergeBelow : function()
31079     {
31080         var table = this.toTableArray();
31081         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
31082             return; // no row below
31083         }
31084         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
31085             return; // nothing right?
31086         }
31087         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
31088         
31089         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
31090             return; // right hand side is not same rowspan.
31091         }
31092         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
31093         rc.cell.parentNode.removeChild(rc.cell);
31094         this.rowspan += rc.rowspan;
31095         this.node.setAttribute('rowspan', this.rowspan);
31096     },
31097     
31098     split: function()
31099     {
31100         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
31101             return;
31102         }
31103         var table = this.toTableArray();
31104         var cd = this.cellData;
31105         this.rowspan = 1;
31106         this.colspan = 1;
31107         
31108         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
31109              
31110             
31111             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
31112                 if (r == cd.row && c == cd.col) {
31113                     this.node.removeAttribute('rowspan');
31114                     this.node.removeAttribute('colspan');
31115                 }
31116                  
31117                 var ntd = this.node.cloneNode(); // which col/row should be 0..
31118                 ntd.removeAttribute('id'); 
31119                 ntd.style.width  = this.colWidths[c];
31120                 ntd.innerHTML = '';
31121                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
31122             }
31123             
31124         }
31125         this.redrawAllCells(table);
31126         
31127     },
31128     
31129     
31130     
31131     redrawAllCells: function(table)
31132     {
31133         
31134          
31135         var tab = this.node.closest('tr').closest('table');
31136         var ctr = tab.rows[0].parentNode;
31137         Array.from(tab.rows).forEach(function(r, ri){
31138             
31139             Array.from(r.cells).forEach(function(ce, ci){
31140                 ce.parentNode.removeChild(ce);
31141             });
31142             r.parentNode.removeChild(r);
31143         });
31144         for(var r = 0 ; r < table.length; r++) {
31145             var re = tab.rows[r];
31146             
31147             var re = tab.ownerDocument.createElement('tr');
31148             ctr.appendChild(re);
31149             for(var c = 0 ; c < table[r].length; c++) {
31150                 if (table[r][c].cell === false) {
31151                     continue;
31152                 }
31153                 
31154                 re.appendChild(table[r][c].cell);
31155                  
31156                 table[r][c].cell = false;
31157             }
31158         }
31159         
31160     },
31161     updateWidths : function(table)
31162     {
31163         for(var r = 0 ; r < table.length; r++) {
31164            
31165             for(var c = 0 ; c < table[r].length; c++) {
31166                 if (table[r][c].cell === false) {
31167                     continue;
31168                 }
31169                 
31170                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
31171                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
31172                     el.width = Math.floor(this.colWidths[c])  +'%';
31173                     el.updateElement(el.node);
31174                 }
31175                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
31176                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
31177                     var width = 0;
31178                     for(var i = 0; i < table[r][c].colspan; i ++) {
31179                         width += Math.floor(this.colWidths[c + i]);
31180                     }
31181                     el.width = width  +'%';
31182                     el.updateElement(el.node);
31183                 }
31184                 table[r][c].cell = false; // done
31185             }
31186         }
31187     },
31188     normalizeWidths : function(table)
31189     {
31190         if (this.colWidths[0] === false) {
31191             var nw = 100.0 / this.colWidths.length;
31192             this.colWidths.forEach(function(w,i) {
31193                 this.colWidths[i] = nw;
31194             },this);
31195             return;
31196         }
31197     
31198         var t = 0, missing = [];
31199         
31200         this.colWidths.forEach(function(w,i) {
31201             //if you mix % and
31202             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
31203             var add =  this.colWidths[i];
31204             if (add > 0) {
31205                 t+=add;
31206                 return;
31207             }
31208             missing.push(i);
31209             
31210             
31211         },this);
31212         var nc = this.colWidths.length;
31213         if (missing.length) {
31214             var mult = (nc - missing.length) / (1.0 * nc);
31215             var t = mult * t;
31216             var ew = (100 -t) / (1.0 * missing.length);
31217             this.colWidths.forEach(function(w,i) {
31218                 if (w > 0) {
31219                     this.colWidths[i] = w * mult;
31220                     return;
31221                 }
31222                 
31223                 this.colWidths[i] = ew;
31224             }, this);
31225             // have to make up numbers..
31226              
31227         }
31228         // now we should have all the widths..
31229         
31230     
31231     },
31232     
31233     shrinkColumn : function()
31234     {
31235         var table = this.toTableArray();
31236         this.normalizeWidths(table);
31237         var col = this.cellData.col;
31238         var nw = this.colWidths[col] * 0.8;
31239         if (nw < 5) {
31240             return;
31241         }
31242         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
31243         this.colWidths.forEach(function(w,i) {
31244             if (i == col) {
31245                  this.colWidths[i] = nw;
31246                 return;
31247             }
31248             this.colWidths[i] += otherAdd
31249         }, this);
31250         this.updateWidths(table);
31251          
31252     },
31253     growColumn : function()
31254     {
31255         var table = this.toTableArray();
31256         this.normalizeWidths(table);
31257         var col = this.cellData.col;
31258         var nw = this.colWidths[col] * 1.2;
31259         if (nw > 90) {
31260             return;
31261         }
31262         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
31263         this.colWidths.forEach(function(w,i) {
31264             if (i == col) {
31265                 this.colWidths[i] = nw;
31266                 return;
31267             }
31268             this.colWidths[i] -= otherSub
31269         }, this);
31270         this.updateWidths(table);
31271          
31272     },
31273     deleteRow : function()
31274     {
31275         // delete this rows 'tr'
31276         // if any of the cells in this row have a rowspan > 1 && row!= this row..
31277         // then reduce the rowspan.
31278         var table = this.toTableArray();
31279         // this.cellData.row;
31280         for (var i =0;i< table[this.cellData.row].length ; i++) {
31281             var c = table[this.cellData.row][i];
31282             if (c.row != this.cellData.row) {
31283                 
31284                 c.rowspan--;
31285                 c.cell.setAttribute('rowspan', c.rowspan);
31286                 continue;
31287             }
31288             if (c.rowspan > 1) {
31289                 c.rowspan--;
31290                 c.cell.setAttribute('rowspan', c.rowspan);
31291             }
31292         }
31293         table.splice(this.cellData.row,1);
31294         this.redrawAllCells(table);
31295         
31296     },
31297     deleteColumn : function()
31298     {
31299         var table = this.toTableArray();
31300         
31301         for (var i =0;i< table.length ; i++) {
31302             var c = table[i][this.cellData.col];
31303             if (c.col != this.cellData.col) {
31304                 table[i][this.cellData.col].colspan--;
31305             } else if (c.colspan > 1) {
31306                 c.colspan--;
31307                 c.cell.setAttribute('colspan', c.colspan);
31308             }
31309             table[i].splice(this.cellData.col,1);
31310         }
31311         
31312         this.redrawAllCells(table);
31313     }
31314     
31315     
31316     
31317     
31318 })
31319
31320 //<script type="text/javascript">
31321
31322 /*
31323  * Based  Ext JS Library 1.1.1
31324  * Copyright(c) 2006-2007, Ext JS, LLC.
31325  * LGPL
31326  *
31327  */
31328  
31329 /**
31330  * @class Roo.HtmlEditorCore
31331  * @extends Roo.Component
31332  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
31333  *
31334  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
31335  */
31336
31337 Roo.HtmlEditorCore = function(config){
31338     
31339     
31340     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
31341     
31342     
31343     this.addEvents({
31344         /**
31345          * @event initialize
31346          * Fires when the editor is fully initialized (including the iframe)
31347          * @param {Roo.HtmlEditorCore} this
31348          */
31349         initialize: true,
31350         /**
31351          * @event activate
31352          * Fires when the editor is first receives the focus. Any insertion must wait
31353          * until after this event.
31354          * @param {Roo.HtmlEditorCore} this
31355          */
31356         activate: true,
31357          /**
31358          * @event beforesync
31359          * Fires before the textarea is updated with content from the editor iframe. Return false
31360          * to cancel the sync.
31361          * @param {Roo.HtmlEditorCore} this
31362          * @param {String} html
31363          */
31364         beforesync: true,
31365          /**
31366          * @event beforepush
31367          * Fires before the iframe editor is updated with content from the textarea. Return false
31368          * to cancel the push.
31369          * @param {Roo.HtmlEditorCore} this
31370          * @param {String} html
31371          */
31372         beforepush: true,
31373          /**
31374          * @event sync
31375          * Fires when the textarea is updated with content from the editor iframe.
31376          * @param {Roo.HtmlEditorCore} this
31377          * @param {String} html
31378          */
31379         sync: true,
31380          /**
31381          * @event push
31382          * Fires when the iframe editor is updated with content from the textarea.
31383          * @param {Roo.HtmlEditorCore} this
31384          * @param {String} html
31385          */
31386         push: true,
31387         
31388         /**
31389          * @event editorevent
31390          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31391          * @param {Roo.HtmlEditorCore} this
31392          */
31393         editorevent: true 
31394         
31395         
31396     });
31397     
31398     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
31399     
31400     // defaults : white / black...
31401     this.applyBlacklists();
31402     
31403     
31404     
31405 };
31406
31407
31408 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
31409
31410
31411      /**
31412      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
31413      */
31414     
31415     owner : false,
31416     
31417      /**
31418      * @cfg {String} css styling for resizing. (used on bootstrap only)
31419      */
31420     resize : false,
31421      /**
31422      * @cfg {Number} height (in pixels)
31423      */   
31424     height: 300,
31425    /**
31426      * @cfg {Number} width (in pixels)
31427      */   
31428     width: 500,
31429      /**
31430      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
31431      *         if you are doing an email editor, this probably needs disabling, it's designed
31432      */
31433     autoClean: true,
31434     
31435     /**
31436      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
31437      */
31438     enableBlocks : true,
31439     /**
31440      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
31441      * 
31442      */
31443     stylesheets: false,
31444      /**
31445      * @cfg {String} language default en - language of text (usefull for rtl languages)
31446      * 
31447      */
31448     language: 'en',
31449     
31450     /**
31451      * @cfg {boolean} allowComments - default false - allow comments in HTML source
31452      *          - by default they are stripped - if you are editing email you may need this.
31453      */
31454     allowComments: false,
31455     // id of frame..
31456     frameId: false,
31457     
31458     // private properties
31459     validationEvent : false,
31460     deferHeight: true,
31461     initialized : false,
31462     activated : false,
31463     sourceEditMode : false,
31464     onFocus : Roo.emptyFn,
31465     iframePad:3,
31466     hideMode:'offsets',
31467     
31468     clearUp: true,
31469     
31470     // blacklist + whitelisted elements..
31471     black: false,
31472     white: false,
31473      
31474     bodyCls : '',
31475
31476     
31477     undoManager : false,
31478     /**
31479      * Protected method that will not generally be called directly. It
31480      * is called when the editor initializes the iframe with HTML contents. Override this method if you
31481      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
31482      */
31483     getDocMarkup : function(){
31484         // body styles..
31485         var st = '';
31486         
31487         // inherit styels from page...?? 
31488         if (this.stylesheets === false) {
31489             
31490             Roo.get(document.head).select('style').each(function(node) {
31491                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
31492             });
31493             
31494             Roo.get(document.head).select('link').each(function(node) { 
31495                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
31496             });
31497             
31498         } else if (!this.stylesheets.length) {
31499                 // simple..
31500                 st = '<style type="text/css">' +
31501                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
31502                    '</style>';
31503         } else {
31504             for (var i in this.stylesheets) {
31505                 if (typeof(this.stylesheets[i]) != 'string') {
31506                     continue;
31507                 }
31508                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
31509             }
31510             
31511         }
31512         
31513         st +=  '<style type="text/css">' +
31514             'IMG { cursor: pointer } ' +
31515         '</style>';
31516         
31517         st += '<meta name="google" content="notranslate">';
31518         
31519         var cls = 'notranslate roo-htmleditor-body';
31520         
31521         if(this.bodyCls.length){
31522             cls += ' ' + this.bodyCls;
31523         }
31524         
31525         return '<html  class="notranslate" translate="no"><head>' + st  +
31526             //<style type="text/css">' +
31527             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
31528             //'</style>' +
31529             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
31530     },
31531
31532     // private
31533     onRender : function(ct, position)
31534     {
31535         var _t = this;
31536         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
31537         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
31538         
31539         
31540         this.el.dom.style.border = '0 none';
31541         this.el.dom.setAttribute('tabIndex', -1);
31542         this.el.addClass('x-hidden hide');
31543         
31544         
31545         
31546         if(Roo.isIE){ // fix IE 1px bogus margin
31547             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31548         }
31549        
31550         
31551         this.frameId = Roo.id();
31552         
31553         var ifcfg = {
31554             tag: 'iframe',
31555             cls: 'form-control', // bootstrap..
31556             id: this.frameId,
31557             name: this.frameId,
31558             frameBorder : 'no',
31559             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
31560         };
31561         if (this.resize) {
31562             ifcfg.style = { resize : this.resize };
31563         }
31564         
31565         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
31566         
31567         
31568         this.iframe = iframe.dom;
31569
31570         this.assignDocWin();
31571         
31572         this.doc.designMode = 'on';
31573        
31574         this.doc.open();
31575         this.doc.write(this.getDocMarkup());
31576         this.doc.close();
31577
31578         
31579         var task = { // must defer to wait for browser to be ready
31580             run : function(){
31581                 //console.log("run task?" + this.doc.readyState);
31582                 this.assignDocWin();
31583                 if(this.doc.body || this.doc.readyState == 'complete'){
31584                     try {
31585                         this.doc.designMode="on";
31586                         
31587                     } catch (e) {
31588                         return;
31589                     }
31590                     Roo.TaskMgr.stop(task);
31591                     this.initEditor.defer(10, this);
31592                 }
31593             },
31594             interval : 10,
31595             duration: 10000,
31596             scope: this
31597         };
31598         Roo.TaskMgr.start(task);
31599
31600     },
31601
31602     // private
31603     onResize : function(w, h)
31604     {
31605          Roo.log('resize: ' +w + ',' + h );
31606         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31607         if(!this.iframe){
31608             return;
31609         }
31610         if(typeof w == 'number'){
31611             
31612             this.iframe.style.width = w + 'px';
31613         }
31614         if(typeof h == 'number'){
31615             
31616             this.iframe.style.height = h + 'px';
31617             if(this.doc){
31618                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31619             }
31620         }
31621         
31622     },
31623
31624     /**
31625      * Toggles the editor between standard and source edit mode.
31626      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31627      */
31628     toggleSourceEdit : function(sourceEditMode){
31629         
31630         this.sourceEditMode = sourceEditMode === true;
31631         
31632         if(this.sourceEditMode){
31633  
31634             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31635             
31636         }else{
31637             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31638             //this.iframe.className = '';
31639             this.deferFocus();
31640         }
31641         //this.setSize(this.owner.wrap.getSize());
31642         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31643     },
31644
31645     
31646   
31647
31648     /**
31649      * Protected method that will not generally be called directly. If you need/want
31650      * custom HTML cleanup, this is the method you should override.
31651      * @param {String} html The HTML to be cleaned
31652      * return {String} The cleaned HTML
31653      */
31654     cleanHtml : function(html)
31655     {
31656         html = String(html);
31657         if(html.length > 5){
31658             if(Roo.isSafari){ // strip safari nonsense
31659                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31660             }
31661         }
31662         if(html == '&nbsp;'){
31663             html = '';
31664         }
31665         return html;
31666     },
31667
31668     /**
31669      * HTML Editor -> Textarea
31670      * Protected method that will not generally be called directly. Syncs the contents
31671      * of the editor iframe with the textarea.
31672      */
31673     syncValue : function()
31674     {
31675         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31676         if(this.initialized){
31677             
31678             if (this.undoManager) {
31679                 this.undoManager.addEvent();
31680             }
31681
31682             
31683             var bd = (this.doc.body || this.doc.documentElement);
31684            
31685             
31686             var sel = this.win.getSelection();
31687             
31688             var div = document.createElement('div');
31689             div.innerHTML = bd.innerHTML;
31690             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31691             if (gtx.length > 0) {
31692                 var rm = gtx.item(0).parentNode;
31693                 rm.parentNode.removeChild(rm);
31694             }
31695             
31696            
31697             if (this.enableBlocks) {
31698                 new Roo.htmleditor.FilterBlock({ node : div });
31699             }
31700             
31701             var html = div.innerHTML;
31702             
31703             //?? tidy?
31704             if (this.autoClean) {
31705                 
31706                 new Roo.htmleditor.FilterAttributes({
31707                     node : div,
31708                     attrib_white : [
31709                             'href',
31710                             'src',
31711                             'name',
31712                             'align',
31713                             'colspan',
31714                             'rowspan',
31715                             'data-display',
31716                             'data-caption-display',
31717                             'data-width',
31718                             'data-caption',
31719                             'start' ,
31720                             'style',
31721                             // youtube embed.
31722                             'class',
31723                             'allowfullscreen',
31724                             'frameborder',
31725                             'width',
31726                             'height',
31727                             'alt'
31728                             ],
31729                     attrib_clean : ['href', 'src' ] 
31730                 });
31731                 
31732                 var tidy = new Roo.htmleditor.TidySerializer({
31733                     inner:  true
31734                 });
31735                 html  = tidy.serialize(div);
31736                 
31737             }
31738             
31739             
31740             if(Roo.isSafari){
31741                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31742                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31743                 if(m && m[1]){
31744                     html = '<div style="'+m[0]+'">' + html + '</div>';
31745                 }
31746             }
31747             html = this.cleanHtml(html);
31748             // fix up the special chars.. normaly like back quotes in word...
31749             // however we do not want to do this with chinese..
31750             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31751                 
31752                 var cc = match.charCodeAt();
31753
31754                 // Get the character value, handling surrogate pairs
31755                 if (match.length == 2) {
31756                     // It's a surrogate pair, calculate the Unicode code point
31757                     var high = match.charCodeAt(0) - 0xD800;
31758                     var low  = match.charCodeAt(1) - 0xDC00;
31759                     cc = (high * 0x400) + low + 0x10000;
31760                 }  else if (
31761                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31762                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31763                     (cc >= 0xf900 && cc < 0xfb00 )
31764                 ) {
31765                         return match;
31766                 }  
31767          
31768                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31769                 return "&#" + cc + ";";
31770                 
31771                 
31772             });
31773             
31774             
31775              
31776             if(this.owner.fireEvent('beforesync', this, html) !== false){
31777                 this.el.dom.value = html;
31778                 this.owner.fireEvent('sync', this, html);
31779             }
31780         }
31781     },
31782
31783     /**
31784      * TEXTAREA -> EDITABLE
31785      * Protected method that will not generally be called directly. Pushes the value of the textarea
31786      * into the iframe editor.
31787      */
31788     pushValue : function()
31789     {
31790         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31791         if(this.initialized){
31792             var v = this.el.dom.value.trim();
31793             
31794             
31795             if(this.owner.fireEvent('beforepush', this, v) !== false){
31796                 var d = (this.doc.body || this.doc.documentElement);
31797                 d.innerHTML = v;
31798                  
31799                 this.el.dom.value = d.innerHTML;
31800                 this.owner.fireEvent('push', this, v);
31801             }
31802             if (this.autoClean) {
31803                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31804                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31805             }
31806             if (this.enableBlocks) {
31807                 Roo.htmleditor.Block.initAll(this.doc.body);
31808             }
31809             
31810             this.updateLanguage();
31811             
31812             var lc = this.doc.body.lastChild;
31813             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31814                 // add an extra line at the end.
31815                 this.doc.body.appendChild(this.doc.createElement('br'));
31816             }
31817             
31818             
31819         }
31820     },
31821
31822     // private
31823     deferFocus : function(){
31824         this.focus.defer(10, this);
31825     },
31826
31827     // doc'ed in Field
31828     focus : function(){
31829         if(this.win && !this.sourceEditMode){
31830             this.win.focus();
31831         }else{
31832             this.el.focus();
31833         }
31834     },
31835     
31836     assignDocWin: function()
31837     {
31838         var iframe = this.iframe;
31839         
31840          if(Roo.isIE){
31841             this.doc = iframe.contentWindow.document;
31842             this.win = iframe.contentWindow;
31843         } else {
31844 //            if (!Roo.get(this.frameId)) {
31845 //                return;
31846 //            }
31847 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31848 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31849             
31850             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31851                 return;
31852             }
31853             
31854             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31855             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31856         }
31857     },
31858     
31859     // private
31860     initEditor : function(){
31861         //console.log("INIT EDITOR");
31862         this.assignDocWin();
31863         
31864         
31865         
31866         this.doc.designMode="on";
31867         this.doc.open();
31868         this.doc.write(this.getDocMarkup());
31869         this.doc.close();
31870         
31871         var dbody = (this.doc.body || this.doc.documentElement);
31872         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31873         // this copies styles from the containing element into thsi one..
31874         // not sure why we need all of this..
31875         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31876         
31877         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31878         //ss['background-attachment'] = 'fixed'; // w3c
31879         dbody.bgProperties = 'fixed'; // ie
31880         dbody.setAttribute("translate", "no");
31881         
31882         //Roo.DomHelper.applyStyles(dbody, ss);
31883         Roo.EventManager.on(this.doc, {
31884              
31885             'mouseup': this.onEditorEvent,
31886             'dblclick': this.onEditorEvent,
31887             'click': this.onEditorEvent,
31888             'keyup': this.onEditorEvent,
31889             
31890             buffer:100,
31891             scope: this
31892         });
31893         Roo.EventManager.on(this.doc, {
31894             'paste': this.onPasteEvent,
31895             scope : this
31896         });
31897         if(Roo.isGecko){
31898             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31899         }
31900         //??? needed???
31901         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31902             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31903         }
31904         this.initialized = true;
31905
31906         
31907         // initialize special key events - enter
31908         new Roo.htmleditor.KeyEnter({core : this});
31909         
31910          
31911         
31912         this.owner.fireEvent('initialize', this);
31913         this.pushValue();
31914     },
31915     // this is to prevent a href clicks resulting in a redirect?
31916    
31917     onPasteEvent : function(e,v)
31918     {
31919         // I think we better assume paste is going to be a dirty load of rubish from word..
31920         
31921         // even pasting into a 'email version' of this widget will have to clean up that mess.
31922         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31923         
31924         // check what type of paste - if it's an image, then handle it differently.
31925         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31926             // pasting images? 
31927             var urlAPI = (window.createObjectURL && window) || 
31928                 (window.URL && URL.revokeObjectURL && URL) || 
31929                 (window.webkitURL && webkitURL);
31930             
31931             var r = new FileReader();
31932             var t = this;
31933             r.addEventListener('load',function()
31934             {
31935                 
31936                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31937                 // is insert asycn?
31938                 if (t.enableBlocks) {
31939                     
31940                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31941                         if (img.closest('figure')) { // assume!! that it's aready
31942                             return;
31943                         }
31944                         var fig  = new Roo.htmleditor.BlockFigure({
31945                             image_src  : img.src
31946                         });
31947                         fig.updateElement(img); // replace it..
31948                         
31949                     });
31950                 }
31951                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31952                 t.owner.fireEvent('paste', this);
31953             });
31954             r.readAsDataURL(cd.files[0]);
31955             
31956             e.preventDefault();
31957             
31958             return false;
31959         }
31960         if (cd.types.indexOf('text/html') < 0 ) {
31961             return false;
31962         }
31963         var images = [];
31964         var html = cd.getData('text/html'); // clipboard event
31965         if (cd.types.indexOf('text/rtf') > -1) {
31966             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31967             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31968         }
31969         // Roo.log(images);
31970         // Roo.log(imgs);
31971         // fixme..
31972         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31973                        .map(function(g) { return g.toDataURL(); })
31974                        .filter(function(g) { return g != 'about:blank'; });
31975         
31976         //Roo.log(html);
31977         html = this.cleanWordChars(html);
31978         
31979         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31980         
31981         
31982         var sn = this.getParentElement();
31983         // check if d contains a table, and prevent nesting??
31984         //Roo.log(d.getElementsByTagName('table'));
31985         //Roo.log(sn);
31986         //Roo.log(sn.closest('table'));
31987         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31988             e.preventDefault();
31989             this.insertAtCursor("You can not nest tables");
31990             //Roo.log("prevent?"); // fixme - 
31991             return false;
31992         }
31993         
31994         
31995         
31996         if (images.length > 0) {
31997             // replace all v:imagedata - with img.
31998             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31999             Roo.each(ar, function(node) {
32000                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
32001                 node.parentNode.removeChild(node);
32002             });
32003             
32004             
32005             Roo.each(d.getElementsByTagName('img'), function(img, i) {
32006                 img.setAttribute('src', images[i]);
32007             });
32008         }
32009         if (this.autoClean) {
32010             new Roo.htmleditor.FilterWord({ node : d });
32011             
32012             new Roo.htmleditor.FilterStyleToTag({ node : d });
32013             new Roo.htmleditor.FilterAttributes({
32014                 node : d,
32015                 attrib_white : [
32016                     'href',
32017                     'src',
32018                     'name',
32019                     'align',
32020                     'colspan',
32021                     'rowspan' 
32022                 /*  THESE ARE NOT ALLWOED FOR PASTE
32023                  *    'data-display',
32024                     'data-caption-display',
32025                     'data-width',
32026                     'data-caption',
32027                     'start' ,
32028                     'style',
32029                     // youtube embed.
32030                     'class',
32031                     'allowfullscreen',
32032                     'frameborder',
32033                     'width',
32034                     'height',
32035                     'alt'
32036                     */
32037                     ],
32038                 attrib_clean : ['href', 'src' ] 
32039             });
32040             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
32041             // should be fonts..
32042             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
32043             new Roo.htmleditor.FilterParagraph({ node : d });
32044             new Roo.htmleditor.FilterSpan({ node : d });
32045             new Roo.htmleditor.FilterLongBr({ node : d });
32046             new Roo.htmleditor.FilterComment({ node : d });
32047             
32048             
32049         }
32050         if (this.enableBlocks) {
32051                 
32052             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
32053                 if (img.closest('figure')) { // assume!! that it's aready
32054                     return;
32055                 }
32056                 var fig  = new Roo.htmleditor.BlockFigure({
32057                     image_src  : img.src
32058                 });
32059                 fig.updateElement(img); // replace it..
32060                 
32061             });
32062         }
32063         
32064         
32065         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
32066         if (this.enableBlocks) {
32067             Roo.htmleditor.Block.initAll(this.doc.body);
32068         }
32069          
32070         
32071         e.preventDefault();
32072         this.owner.fireEvent('paste', this);
32073         return false;
32074         // default behaveiour should be our local cleanup paste? (optional?)
32075         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
32076         //this.owner.fireEvent('paste', e, v);
32077     },
32078     // private
32079     onDestroy : function(){
32080         
32081         
32082         
32083         if(this.rendered){
32084             
32085             //for (var i =0; i < this.toolbars.length;i++) {
32086             //    // fixme - ask toolbars for heights?
32087             //    this.toolbars[i].onDestroy();
32088            // }
32089             
32090             //this.wrap.dom.innerHTML = '';
32091             //this.wrap.remove();
32092         }
32093     },
32094
32095     // private
32096     onFirstFocus : function(){
32097         
32098         this.assignDocWin();
32099         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
32100         
32101         this.activated = true;
32102          
32103     
32104         if(Roo.isGecko){ // prevent silly gecko errors
32105             this.win.focus();
32106             var s = this.win.getSelection();
32107             if(!s.focusNode || s.focusNode.nodeType != 3){
32108                 var r = s.getRangeAt(0);
32109                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
32110                 r.collapse(true);
32111                 this.deferFocus();
32112             }
32113             try{
32114                 this.execCmd('useCSS', true);
32115                 this.execCmd('styleWithCSS', false);
32116             }catch(e){}
32117         }
32118         this.owner.fireEvent('activate', this);
32119     },
32120
32121     // private
32122     adjustFont: function(btn){
32123         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
32124         //if(Roo.isSafari){ // safari
32125         //    adjust *= 2;
32126        // }
32127         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
32128         if(Roo.isSafari){ // safari
32129             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
32130             v =  (v < 10) ? 10 : v;
32131             v =  (v > 48) ? 48 : v;
32132             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
32133             
32134         }
32135         
32136         
32137         v = Math.max(1, v+adjust);
32138         
32139         this.execCmd('FontSize', v  );
32140     },
32141
32142     onEditorEvent : function(e)
32143     {
32144          
32145         
32146         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
32147             return; // we do not handle this.. (undo manager does..)
32148         }
32149         // clicking a 'block'?
32150         
32151         // in theory this detects if the last element is not a br, then we try and do that.
32152         // its so clicking in space at bottom triggers adding a br and moving the cursor.
32153         if (e &&
32154             e.target.nodeName == 'BODY' &&
32155             e.type == "mouseup" &&
32156             this.doc.body.lastChild
32157            ) {
32158             var lc = this.doc.body.lastChild;
32159             // gtx-trans is google translate plugin adding crap.
32160             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
32161                 lc = lc.previousSibling;
32162             }
32163             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
32164             // if last element is <BR> - then dont do anything.
32165             
32166                 var ns = this.doc.createElement('br');
32167                 this.doc.body.appendChild(ns);
32168                 range = this.doc.createRange();
32169                 range.setStartAfter(ns);
32170                 range.collapse(true);
32171                 var sel = this.win.getSelection();
32172                 sel.removeAllRanges();
32173                 sel.addRange(range);
32174             }
32175         }
32176         
32177         
32178         
32179         this.fireEditorEvent(e);
32180       //  this.updateToolbar();
32181         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
32182     },
32183     
32184     fireEditorEvent: function(e)
32185     {
32186         this.owner.fireEvent('editorevent', this, e);
32187     },
32188
32189     insertTag : function(tg)
32190     {
32191         // could be a bit smarter... -> wrap the current selected tRoo..
32192         if (tg.toLowerCase() == 'span' ||
32193             tg.toLowerCase() == 'code' ||
32194             tg.toLowerCase() == 'sup' ||
32195             tg.toLowerCase() == 'sub' 
32196             ) {
32197             
32198             range = this.createRange(this.getSelection());
32199             var wrappingNode = this.doc.createElement(tg.toLowerCase());
32200             wrappingNode.appendChild(range.extractContents());
32201             range.insertNode(wrappingNode);
32202
32203             return;
32204             
32205             
32206             
32207         }
32208         this.execCmd("formatblock",   tg);
32209         this.undoManager.addEvent(); 
32210     },
32211     
32212     insertText : function(txt)
32213     {
32214         
32215         
32216         var range = this.createRange();
32217         range.deleteContents();
32218                //alert(Sender.getAttribute('label'));
32219                
32220         range.insertNode(this.doc.createTextNode(txt));
32221         this.undoManager.addEvent();
32222     } ,
32223     
32224      
32225
32226     /**
32227      * Executes a Midas editor command on the editor document and performs necessary focus and
32228      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
32229      * @param {String} cmd The Midas command
32230      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
32231      */
32232     relayCmd : function(cmd, value)
32233     {
32234         
32235         switch (cmd) {
32236             case 'justifyleft':
32237             case 'justifyright':
32238             case 'justifycenter':
32239                 // if we are in a cell, then we will adjust the
32240                 var n = this.getParentElement();
32241                 var td = n.closest('td');
32242                 if (td) {
32243                     var bl = Roo.htmleditor.Block.factory(td);
32244                     bl.textAlign = cmd.replace('justify','');
32245                     bl.updateElement();
32246                     this.owner.fireEvent('editorevent', this);
32247                     return;
32248                 }
32249                 this.execCmd('styleWithCSS', true); // 
32250                 break;
32251             case 'bold':
32252             case 'italic':
32253             case 'underline':                
32254                 // if there is no selection, then we insert, and set the curson inside it..
32255                 this.execCmd('styleWithCSS', false); 
32256                 break;
32257                 
32258         
32259             default:
32260                 break;
32261         }
32262         
32263         
32264         this.win.focus();
32265         this.execCmd(cmd, value);
32266         this.owner.fireEvent('editorevent', this);
32267         //this.updateToolbar();
32268         this.owner.deferFocus();
32269     },
32270
32271     /**
32272      * Executes a Midas editor command directly on the editor document.
32273      * For visual commands, you should use {@link #relayCmd} instead.
32274      * <b>This should only be called after the editor is initialized.</b>
32275      * @param {String} cmd The Midas command
32276      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
32277      */
32278     execCmd : function(cmd, value){
32279         this.doc.execCommand(cmd, false, value === undefined ? null : value);
32280         this.syncValue();
32281     },
32282  
32283  
32284    
32285     /**
32286      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
32287      * to insert tRoo.
32288      * @param {String} text | dom node.. 
32289      */
32290     insertAtCursor : function(text)
32291     {
32292         
32293         if(!this.activated){
32294             return;
32295         }
32296          
32297         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
32298             this.win.focus();
32299             
32300             
32301             // from jquery ui (MIT licenced)
32302             var range, node;
32303             var win = this.win;
32304             
32305             if (win.getSelection && win.getSelection().getRangeAt) {
32306                 
32307                 // delete the existing?
32308                 
32309                 this.createRange(this.getSelection()).deleteContents();
32310                 range = win.getSelection().getRangeAt(0);
32311                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
32312                 range.insertNode(node);
32313                 range = range.cloneRange();
32314                 range.collapse(false);
32315                  
32316                 win.getSelection().removeAllRanges();
32317                 win.getSelection().addRange(range);
32318                 
32319                 
32320                 
32321             } else if (win.document.selection && win.document.selection.createRange) {
32322                 // no firefox support
32323                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
32324                 win.document.selection.createRange().pasteHTML(txt);
32325             
32326             } else {
32327                 // no firefox support
32328                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
32329                 this.execCmd('InsertHTML', txt);
32330             } 
32331             this.syncValue();
32332             
32333             this.deferFocus();
32334         }
32335     },
32336  // private
32337     mozKeyPress : function(e){
32338         if(e.ctrlKey){
32339             var c = e.getCharCode(), cmd;
32340           
32341             if(c > 0){
32342                 c = String.fromCharCode(c).toLowerCase();
32343                 switch(c){
32344                     case 'b':
32345                         cmd = 'bold';
32346                         break;
32347                     case 'i':
32348                         cmd = 'italic';
32349                         break;
32350                     
32351                     case 'u':
32352                         cmd = 'underline';
32353                         break;
32354                     
32355                     //case 'v':
32356                       //  this.cleanUpPaste.defer(100, this);
32357                       //  return;
32358                         
32359                 }
32360                 if(cmd){
32361                     
32362                     this.relayCmd(cmd);
32363                     //this.win.focus();
32364                     //this.execCmd(cmd);
32365                     //this.deferFocus();
32366                     e.preventDefault();
32367                 }
32368                 
32369             }
32370         }
32371     },
32372
32373     // private
32374     fixKeys : function(){ // load time branching for fastest keydown performance
32375         
32376         
32377         if(Roo.isIE){
32378             return function(e){
32379                 var k = e.getKey(), r;
32380                 if(k == e.TAB){
32381                     e.stopEvent();
32382                     r = this.doc.selection.createRange();
32383                     if(r){
32384                         r.collapse(true);
32385                         r.pasteHTML('&#160;&#160;&#160;&#160;');
32386                         this.deferFocus();
32387                     }
32388                     return;
32389                 }
32390                 /// this is handled by Roo.htmleditor.KeyEnter
32391                  /*
32392                 if(k == e.ENTER){
32393                     r = this.doc.selection.createRange();
32394                     if(r){
32395                         var target = r.parentElement();
32396                         if(!target || target.tagName.toLowerCase() != 'li'){
32397                             e.stopEvent();
32398                             r.pasteHTML('<br/>');
32399                             r.collapse(false);
32400                             r.select();
32401                         }
32402                     }
32403                 }
32404                 */
32405                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
32406                 //    this.cleanUpPaste.defer(100, this);
32407                 //    return;
32408                 //}
32409                 
32410                 
32411             };
32412         }else if(Roo.isOpera){
32413             return function(e){
32414                 var k = e.getKey();
32415                 if(k == e.TAB){
32416                     e.stopEvent();
32417                     this.win.focus();
32418                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
32419                     this.deferFocus();
32420                 }
32421                
32422                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
32423                 //    this.cleanUpPaste.defer(100, this);
32424                  //   return;
32425                 //}
32426                 
32427             };
32428         }else if(Roo.isSafari){
32429             return function(e){
32430                 var k = e.getKey();
32431                 
32432                 if(k == e.TAB){
32433                     e.stopEvent();
32434                     this.execCmd('InsertText','\t');
32435                     this.deferFocus();
32436                     return;
32437                 }
32438                  this.mozKeyPress(e);
32439                 
32440                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
32441                  //   this.cleanUpPaste.defer(100, this);
32442                  //   return;
32443                // }
32444                 
32445              };
32446         }
32447     }(),
32448     
32449     getAllAncestors: function()
32450     {
32451         var p = this.getSelectedNode();
32452         var a = [];
32453         if (!p) {
32454             a.push(p); // push blank onto stack..
32455             p = this.getParentElement();
32456         }
32457         
32458         
32459         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
32460             a.push(p);
32461             p = p.parentNode;
32462         }
32463         a.push(this.doc.body);
32464         return a;
32465     },
32466     lastSel : false,
32467     lastSelNode : false,
32468     
32469     
32470     getSelection : function() 
32471     {
32472         this.assignDocWin();
32473         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
32474     },
32475     /**
32476      * Select a dom node
32477      * @param {DomElement} node the node to select
32478      */
32479     selectNode : function(node, collapse)
32480     {
32481         var nodeRange = node.ownerDocument.createRange();
32482         try {
32483             nodeRange.selectNode(node);
32484         } catch (e) {
32485             nodeRange.selectNodeContents(node);
32486         }
32487         if (collapse === true) {
32488             nodeRange.collapse(true);
32489         }
32490         //
32491         var s = this.win.getSelection();
32492         s.removeAllRanges();
32493         s.addRange(nodeRange);
32494     },
32495     
32496     getSelectedNode: function() 
32497     {
32498         // this may only work on Gecko!!!
32499         
32500         // should we cache this!!!!
32501         
32502          
32503          
32504         var range = this.createRange(this.getSelection()).cloneRange();
32505         
32506         if (Roo.isIE) {
32507             var parent = range.parentElement();
32508             while (true) {
32509                 var testRange = range.duplicate();
32510                 testRange.moveToElementText(parent);
32511                 if (testRange.inRange(range)) {
32512                     break;
32513                 }
32514                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
32515                     break;
32516                 }
32517                 parent = parent.parentElement;
32518             }
32519             return parent;
32520         }
32521         
32522         // is ancestor a text element.
32523         var ac =  range.commonAncestorContainer;
32524         if (ac.nodeType == 3) {
32525             ac = ac.parentNode;
32526         }
32527         
32528         var ar = ac.childNodes;
32529          
32530         var nodes = [];
32531         var other_nodes = [];
32532         var has_other_nodes = false;
32533         for (var i=0;i<ar.length;i++) {
32534             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
32535                 continue;
32536             }
32537             // fullly contained node.
32538             
32539             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
32540                 nodes.push(ar[i]);
32541                 continue;
32542             }
32543             
32544             // probably selected..
32545             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
32546                 other_nodes.push(ar[i]);
32547                 continue;
32548             }
32549             // outer..
32550             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
32551                 continue;
32552             }
32553             
32554             
32555             has_other_nodes = true;
32556         }
32557         if (!nodes.length && other_nodes.length) {
32558             nodes= other_nodes;
32559         }
32560         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32561             return false;
32562         }
32563         
32564         return nodes[0];
32565     },
32566     
32567     
32568     createRange: function(sel)
32569     {
32570         // this has strange effects when using with 
32571         // top toolbar - not sure if it's a great idea.
32572         //this.editor.contentWindow.focus();
32573         if (typeof sel != "undefined") {
32574             try {
32575                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32576             } catch(e) {
32577                 return this.doc.createRange();
32578             }
32579         } else {
32580             return this.doc.createRange();
32581         }
32582     },
32583     getParentElement: function()
32584     {
32585         
32586         this.assignDocWin();
32587         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32588         
32589         var range = this.createRange(sel);
32590          
32591         try {
32592             var p = range.commonAncestorContainer;
32593             while (p.nodeType == 3) { // text node
32594                 p = p.parentNode;
32595             }
32596             return p;
32597         } catch (e) {
32598             return null;
32599         }
32600     
32601     },
32602     /***
32603      *
32604      * Range intersection.. the hard stuff...
32605      *  '-1' = before
32606      *  '0' = hits..
32607      *  '1' = after.
32608      *         [ -- selected range --- ]
32609      *   [fail]                        [fail]
32610      *
32611      *    basically..
32612      *      if end is before start or  hits it. fail.
32613      *      if start is after end or hits it fail.
32614      *
32615      *   if either hits (but other is outside. - then it's not 
32616      *   
32617      *    
32618      **/
32619     
32620     
32621     // @see http://www.thismuchiknow.co.uk/?p=64.
32622     rangeIntersectsNode : function(range, node)
32623     {
32624         var nodeRange = node.ownerDocument.createRange();
32625         try {
32626             nodeRange.selectNode(node);
32627         } catch (e) {
32628             nodeRange.selectNodeContents(node);
32629         }
32630     
32631         var rangeStartRange = range.cloneRange();
32632         rangeStartRange.collapse(true);
32633     
32634         var rangeEndRange = range.cloneRange();
32635         rangeEndRange.collapse(false);
32636     
32637         var nodeStartRange = nodeRange.cloneRange();
32638         nodeStartRange.collapse(true);
32639     
32640         var nodeEndRange = nodeRange.cloneRange();
32641         nodeEndRange.collapse(false);
32642     
32643         return rangeStartRange.compareBoundaryPoints(
32644                  Range.START_TO_START, nodeEndRange) == -1 &&
32645                rangeEndRange.compareBoundaryPoints(
32646                  Range.START_TO_START, nodeStartRange) == 1;
32647         
32648          
32649     },
32650     rangeCompareNode : function(range, node)
32651     {
32652         var nodeRange = node.ownerDocument.createRange();
32653         try {
32654             nodeRange.selectNode(node);
32655         } catch (e) {
32656             nodeRange.selectNodeContents(node);
32657         }
32658         
32659         
32660         range.collapse(true);
32661     
32662         nodeRange.collapse(true);
32663      
32664         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32665         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32666          
32667         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32668         
32669         var nodeIsBefore   =  ss == 1;
32670         var nodeIsAfter    = ee == -1;
32671         
32672         if (nodeIsBefore && nodeIsAfter) {
32673             return 0; // outer
32674         }
32675         if (!nodeIsBefore && nodeIsAfter) {
32676             return 1; //right trailed.
32677         }
32678         
32679         if (nodeIsBefore && !nodeIsAfter) {
32680             return 2;  // left trailed.
32681         }
32682         // fully contined.
32683         return 3;
32684     },
32685  
32686     cleanWordChars : function(input) {// change the chars to hex code
32687         
32688        var swapCodes  = [ 
32689             [    8211, "&#8211;" ], 
32690             [    8212, "&#8212;" ], 
32691             [    8216,  "'" ],  
32692             [    8217, "'" ],  
32693             [    8220, '"' ],  
32694             [    8221, '"' ],  
32695             [    8226, "*" ],  
32696             [    8230, "..." ]
32697         ]; 
32698         var output = input;
32699         Roo.each(swapCodes, function(sw) { 
32700             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32701             
32702             output = output.replace(swapper, sw[1]);
32703         });
32704         
32705         return output;
32706     },
32707     
32708      
32709     
32710         
32711     
32712     cleanUpChild : function (node)
32713     {
32714         
32715         new Roo.htmleditor.FilterComment({node : node});
32716         new Roo.htmleditor.FilterAttributes({
32717                 node : node,
32718                 attrib_black : this.ablack,
32719                 attrib_clean : this.aclean,
32720                 style_white : this.cwhite,
32721                 style_black : this.cblack
32722         });
32723         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32724         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32725          
32726         
32727     },
32728     
32729     /**
32730      * Clean up MS wordisms...
32731      * @deprecated - use filter directly
32732      */
32733     cleanWord : function(node)
32734     {
32735         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32736         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32737         
32738     },
32739    
32740     
32741     /**
32742
32743      * @deprecated - use filters
32744      */
32745     cleanTableWidths : function(node)
32746     {
32747         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32748         
32749  
32750     },
32751     
32752      
32753         
32754     applyBlacklists : function()
32755     {
32756         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32757         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32758         
32759         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32760         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32761         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32762         
32763         this.white = [];
32764         this.black = [];
32765         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32766             if (b.indexOf(tag) > -1) {
32767                 return;
32768             }
32769             this.white.push(tag);
32770             
32771         }, this);
32772         
32773         Roo.each(w, function(tag) {
32774             if (b.indexOf(tag) > -1) {
32775                 return;
32776             }
32777             if (this.white.indexOf(tag) > -1) {
32778                 return;
32779             }
32780             this.white.push(tag);
32781             
32782         }, this);
32783         
32784         
32785         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32786             if (w.indexOf(tag) > -1) {
32787                 return;
32788             }
32789             this.black.push(tag);
32790             
32791         }, this);
32792         
32793         Roo.each(b, function(tag) {
32794             if (w.indexOf(tag) > -1) {
32795                 return;
32796             }
32797             if (this.black.indexOf(tag) > -1) {
32798                 return;
32799             }
32800             this.black.push(tag);
32801             
32802         }, this);
32803         
32804         
32805         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32806         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32807         
32808         this.cwhite = [];
32809         this.cblack = [];
32810         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32811             if (b.indexOf(tag) > -1) {
32812                 return;
32813             }
32814             this.cwhite.push(tag);
32815             
32816         }, this);
32817         
32818         Roo.each(w, function(tag) {
32819             if (b.indexOf(tag) > -1) {
32820                 return;
32821             }
32822             if (this.cwhite.indexOf(tag) > -1) {
32823                 return;
32824             }
32825             this.cwhite.push(tag);
32826             
32827         }, this);
32828         
32829         
32830         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32831             if (w.indexOf(tag) > -1) {
32832                 return;
32833             }
32834             this.cblack.push(tag);
32835             
32836         }, this);
32837         
32838         Roo.each(b, function(tag) {
32839             if (w.indexOf(tag) > -1) {
32840                 return;
32841             }
32842             if (this.cblack.indexOf(tag) > -1) {
32843                 return;
32844             }
32845             this.cblack.push(tag);
32846             
32847         }, this);
32848     },
32849     
32850     setStylesheets : function(stylesheets)
32851     {
32852         if(typeof(stylesheets) == 'string'){
32853             Roo.get(this.iframe.contentDocument.head).createChild({
32854                 tag : 'link',
32855                 rel : 'stylesheet',
32856                 type : 'text/css',
32857                 href : stylesheets
32858             });
32859             
32860             return;
32861         }
32862         var _this = this;
32863      
32864         Roo.each(stylesheets, function(s) {
32865             if(!s.length){
32866                 return;
32867             }
32868             
32869             Roo.get(_this.iframe.contentDocument.head).createChild({
32870                 tag : 'link',
32871                 rel : 'stylesheet',
32872                 type : 'text/css',
32873                 href : s
32874             });
32875         });
32876
32877         
32878     },
32879     
32880     
32881     updateLanguage : function()
32882     {
32883         if (!this.iframe || !this.iframe.contentDocument) {
32884             return;
32885         }
32886         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32887     },
32888     
32889     
32890     removeStylesheets : function()
32891     {
32892         var _this = this;
32893         
32894         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32895             s.remove();
32896         });
32897     },
32898     
32899     setStyle : function(style)
32900     {
32901         Roo.get(this.iframe.contentDocument.head).createChild({
32902             tag : 'style',
32903             type : 'text/css',
32904             html : style
32905         });
32906
32907         return;
32908     }
32909     
32910     // hide stuff that is not compatible
32911     /**
32912      * @event blur
32913      * @hide
32914      */
32915     /**
32916      * @event change
32917      * @hide
32918      */
32919     /**
32920      * @event focus
32921      * @hide
32922      */
32923     /**
32924      * @event specialkey
32925      * @hide
32926      */
32927     /**
32928      * @cfg {String} fieldClass @hide
32929      */
32930     /**
32931      * @cfg {String} focusClass @hide
32932      */
32933     /**
32934      * @cfg {String} autoCreate @hide
32935      */
32936     /**
32937      * @cfg {String} inputType @hide
32938      */
32939     /**
32940      * @cfg {String} invalidClass @hide
32941      */
32942     /**
32943      * @cfg {String} invalidText @hide
32944      */
32945     /**
32946      * @cfg {String} msgFx @hide
32947      */
32948     /**
32949      * @cfg {String} validateOnBlur @hide
32950      */
32951 });
32952
32953 Roo.HtmlEditorCore.white = [
32954         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32955         
32956        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32957        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32958        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32959        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32960        'TABLE',   'UL',         'XMP', 
32961        
32962        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32963       'THEAD',   'TR', 
32964      
32965       'DIR', 'MENU', 'OL', 'UL', 'DL',
32966        
32967       'EMBED',  'OBJECT'
32968 ];
32969
32970
32971 Roo.HtmlEditorCore.black = [
32972     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32973         'APPLET', // 
32974         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32975         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32976         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32977         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32978         //'FONT' // CLEAN LATER..
32979         'COLGROUP', 'COL'   // messy tables.
32980         
32981         
32982 ];
32983 Roo.HtmlEditorCore.clean = [ // ?? needed???
32984      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32985 ];
32986 Roo.HtmlEditorCore.tag_remove = [
32987     'FONT', 'TBODY'  
32988 ];
32989 // attributes..
32990
32991 Roo.HtmlEditorCore.ablack = [
32992     'on'
32993 ];
32994     
32995 Roo.HtmlEditorCore.aclean = [ 
32996     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32997 ];
32998
32999 // protocols..
33000 Roo.HtmlEditorCore.pwhite= [
33001         'http',  'https',  'mailto'
33002 ];
33003
33004 // white listed style attributes.
33005 Roo.HtmlEditorCore.cwhite= [
33006       //  'text-align', /// default is to allow most things..
33007       
33008          
33009 //        'font-size'//??
33010 ];
33011
33012 // black listed style attributes.
33013 Roo.HtmlEditorCore.cblack= [
33014       //  'font-size' -- this can be set by the project 
33015 ];
33016
33017
33018
33019
33020     /*
33021  * - LGPL
33022  *
33023  * HtmlEditor
33024  * 
33025  */
33026
33027 /**
33028  * @class Roo.bootstrap.form.HtmlEditor
33029  * @extends Roo.bootstrap.form.TextArea
33030  * Bootstrap HtmlEditor class
33031
33032  * @constructor
33033  * Create a new HtmlEditor
33034  * @param {Object} config The config object
33035  */
33036
33037 Roo.bootstrap.form.HtmlEditor = function(config){
33038
33039     this.addEvents({
33040             /**
33041              * @event initialize
33042              * Fires when the editor is fully initialized (including the iframe)
33043              * @param {Roo.bootstrap.form.HtmlEditor} this
33044              */
33045             initialize: true,
33046             /**
33047              * @event activate
33048              * Fires when the editor is first receives the focus. Any insertion must wait
33049              * until after this event.
33050              * @param {Roo.bootstrap.form.HtmlEditor} this
33051              */
33052             activate: true,
33053              /**
33054              * @event beforesync
33055              * Fires before the textarea is updated with content from the editor iframe. Return false
33056              * to cancel the sync.
33057              * @param {Roo.bootstrap.form.HtmlEditor} this
33058              * @param {String} html
33059              */
33060             beforesync: true,
33061              /**
33062              * @event beforepush
33063              * Fires before the iframe editor is updated with content from the textarea. Return false
33064              * to cancel the push.
33065              * @param {Roo.bootstrap.form.HtmlEditor} this
33066              * @param {String} html
33067              */
33068             beforepush: true,
33069              /**
33070              * @event sync
33071              * Fires when the textarea is updated with content from the editor iframe.
33072              * @param {Roo.bootstrap.form.HtmlEditor} this
33073              * @param {String} html
33074              */
33075             sync: true,
33076              /**
33077              * @event push
33078              * Fires when the iframe editor is updated with content from the textarea.
33079              * @param {Roo.bootstrap.form.HtmlEditor} this
33080              * @param {String} html
33081              */
33082             push: true,
33083              /**
33084              * @event editmodechange
33085              * Fires when the editor switches edit modes
33086              * @param {Roo.bootstrap.form.HtmlEditor} this
33087              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
33088              */
33089             editmodechange: true,
33090             /**
33091              * @event editorevent
33092              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
33093              * @param {Roo.bootstrap.form.HtmlEditor} this
33094              */
33095             editorevent: true,
33096             /**
33097              * @event firstfocus
33098              * Fires when on first focus - needed by toolbars..
33099              * @param {Roo.bootstrap.form.HtmlEditor} this
33100              */
33101             firstfocus: true,
33102             /**
33103              * @event autosave
33104              * Auto save the htmlEditor value as a file into Events
33105              * @param {Roo.bootstrap.form.HtmlEditor} this
33106              */
33107             autosave: true,
33108             /**
33109              * @event savedpreview
33110              * preview the saved version of htmlEditor
33111              * @param {Roo.bootstrap.form.HtmlEditor} this
33112              */
33113             savedpreview: true,
33114              /**
33115             * @event stylesheetsclick
33116             * Fires when press the Sytlesheets button
33117             * @param {Roo.HtmlEditorCore} this
33118             */
33119             stylesheetsclick: true,
33120             /**
33121             * @event paste
33122             * Fires when press user pastes into the editor
33123             * @param {Roo.HtmlEditorCore} this
33124             */
33125             paste: true,
33126             /**
33127             * @event imageadd
33128             * Fires when on any editor when an image is added (excluding paste)
33129             * @param {Roo.bootstrap.form.HtmlEditor} this
33130             */
33131            imageadd: true ,
33132             /**
33133             * @event imageupdated
33134             * Fires when on any editor when an image is changed (excluding paste)
33135             * @param {Roo.bootstrap.form.HtmlEditor} this
33136             * @param {HTMLElement} img could also be a figure if blocks are enabled
33137             */
33138            imageupdate: true ,
33139            /**
33140             * @event imagedelete
33141             * Fires when on any editor when an image is deleted
33142             * @param {Roo.bootstrap.form.HtmlEditor} this
33143             * @param {HTMLElement} img could also be a figure if blocks are enabled
33144             * @param {HTMLElement} oldSrc source of image being replaced
33145             */
33146            imagedelete: true  
33147     });
33148     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
33149     if (!this.toolbars) {
33150         this.toolbars = [];
33151     }
33152     
33153     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
33154     
33155 };
33156
33157
33158 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
33159     
33160     
33161       /**
33162      * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
33163      */
33164     toolbars : true,
33165     
33166      /**
33167     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
33168     */
33169     btns : [],
33170    
33171      /**
33172      * @cfg {String} resize  (none|both|horizontal|vertical) - css resize of element
33173      */
33174     resize : false,
33175      /**
33176      * @cfg {Number} height (in pixels)
33177      */   
33178     height: 300,
33179    /**
33180      * @cfg {Number} width (in pixels)
33181      */   
33182     width: false,
33183     
33184     /**
33185      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
33186      * 
33187      */
33188     stylesheets: false,
33189     
33190     // id of frame..
33191     frameId: false,
33192     
33193     // private properties
33194     validationEvent : false,
33195     deferHeight: true,
33196     initialized : false,
33197     activated : false,
33198     
33199     onFocus : Roo.emptyFn,
33200     iframePad:3,
33201     hideMode:'offsets',
33202     
33203     tbContainer : false,
33204     
33205     bodyCls : '',
33206
33207     linkDialogCls : '',
33208     
33209     toolbarContainer :function() {
33210         return this.wrap.select('.x-html-editor-tb',true).first();
33211     },
33212
33213     /**
33214      * Protected method that will not generally be called directly. It
33215      * is called when the editor creates its toolbar. Override this method if you need to
33216      * add custom toolbar buttons.
33217      * @param {HtmlEditor} editor
33218      */
33219     createToolbar : function()
33220     {
33221         //Roo.log('renewing');
33222         //Roo.log("create toolbars");
33223         if (this.toolbars === false) {
33224             return;
33225         }
33226         if (this.toolbars === true) {
33227             this.toolbars = [ 'Standard' ];
33228         }
33229         
33230         var ar = Array.from(this.toolbars);
33231         this.toolbars = [];
33232         ar.forEach(function(t,i) {
33233             if (typeof(t) == 'string') {
33234                 t = {
33235                     xtype : t
33236                 };
33237             }
33238             if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
33239                 t.editor = this;
33240                 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
33241                 t = Roo.factory(t);
33242             }
33243             this.toolbars[i] = t;
33244             this.toolbars[i].render(this.toolbarContainer());
33245         }, this);
33246         
33247         
33248     },
33249
33250      
33251     // private
33252     onRender : function(ct, position)
33253     {
33254        // Roo.log("Call onRender: " + this.xtype);
33255         var _t = this;
33256         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
33257       
33258         this.wrap = this.inputEl().wrap({
33259             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
33260         });
33261         
33262         this.editorcore.onRender(ct, position);
33263          
33264          
33265         this.createToolbar(this);
33266        
33267         
33268           
33269         
33270     },
33271
33272     // private
33273     onResize : function(w, h)
33274     {
33275         Roo.log('resize: ' +w + ',' + h );
33276         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
33277         var ew = false;
33278         var eh = false;
33279         
33280         if(this.inputEl() ){
33281             if(typeof w == 'number'){
33282                 var aw = w - this.wrap.getFrameWidth('lr');
33283                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
33284                 ew = aw;
33285             }
33286             if(typeof h == 'number'){
33287                  var tbh = -11;  // fixme it needs to tool bar size!
33288                 for (var i =0; i < this.toolbars.length;i++) {
33289                     // fixme - ask toolbars for heights?
33290                     tbh += this.toolbars[i].el.getHeight();
33291                     //if (this.toolbars[i].footer) {
33292                     //    tbh += this.toolbars[i].footer.el.getHeight();
33293                     //}
33294                 }
33295               
33296                 
33297                 
33298                 
33299                 
33300                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
33301                 ah -= 5; // knock a few pixes off for look..
33302                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
33303                 var eh = ah;
33304             }
33305         }
33306         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
33307         this.editorcore.onResize(ew,eh);
33308         
33309     },
33310
33311     /**
33312      * Toggles the editor between standard and source edit mode.
33313      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
33314      */
33315     toggleSourceEdit : function(sourceEditMode)
33316     {
33317         this.editorcore.toggleSourceEdit(sourceEditMode);
33318         
33319         if(this.editorcore.sourceEditMode){
33320             Roo.log('editor - showing textarea');
33321             
33322 //            Roo.log('in');
33323 //            Roo.log(this.syncValue());
33324             this.syncValue();
33325             this.inputEl().removeClass(['hide', 'x-hidden']);
33326             this.inputEl().dom.removeAttribute('tabIndex');
33327             this.inputEl().focus();
33328         }else{
33329             Roo.log('editor - hiding textarea');
33330 //            Roo.log('out')
33331 //            Roo.log(this.pushValue()); 
33332             this.pushValue();
33333             
33334             this.inputEl().addClass(['hide', 'x-hidden']);
33335             this.inputEl().dom.setAttribute('tabIndex', -1);
33336             //this.deferFocus();
33337         }
33338          
33339         //if(this.resizable){
33340         //    this.setSize(this.wrap.getSize());
33341         //}
33342         
33343         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
33344     },
33345  
33346     // private (for BoxComponent)
33347     adjustSize : Roo.BoxComponent.prototype.adjustSize,
33348
33349     // private (for BoxComponent)
33350     getResizeEl : function(){
33351         return this.wrap;
33352     },
33353
33354     // private (for BoxComponent)
33355     getPositionEl : function(){
33356         return this.wrap;
33357     },
33358
33359     // private
33360     initEvents : function(){
33361         this.originalValue = this.getValue();
33362     },
33363
33364 //    /**
33365 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
33366 //     * @method
33367 //     */
33368 //    markInvalid : Roo.emptyFn,
33369 //    /**
33370 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
33371 //     * @method
33372 //     */
33373 //    clearInvalid : Roo.emptyFn,
33374
33375     setValue : function(v){
33376         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
33377         this.editorcore.pushValue();
33378     },
33379
33380      
33381     // private
33382     deferFocus : function(){
33383         this.focus.defer(10, this);
33384     },
33385
33386     // doc'ed in Field
33387     focus : function(){
33388         this.editorcore.focus();
33389         
33390     },
33391       
33392
33393     // private
33394     onDestroy : function(){
33395         
33396         
33397         
33398         if(this.rendered){
33399             
33400             for (var i =0; i < this.toolbars.length;i++) {
33401                 // fixme - ask toolbars for heights?
33402                 this.toolbars[i].onDestroy();
33403             }
33404             
33405             this.wrap.dom.innerHTML = '';
33406             this.wrap.remove();
33407         }
33408     },
33409
33410     // private
33411     onFirstFocus : function(){
33412         //Roo.log("onFirstFocus");
33413         this.editorcore.onFirstFocus();
33414          for (var i =0; i < this.toolbars.length;i++) {
33415             this.toolbars[i].onFirstFocus();
33416         }
33417         
33418     },
33419     
33420     // private
33421     syncValue : function()
33422     {   
33423         this.editorcore.syncValue();
33424     },
33425     
33426     pushValue : function()
33427     {   
33428         this.editorcore.pushValue();
33429     }
33430      
33431     
33432     // hide stuff that is not compatible
33433     /**
33434      * @event blur
33435      * @hide
33436      */
33437     /**
33438      * @event change
33439      * @hide
33440      */
33441     /**
33442      * @event focus
33443      * @hide
33444      */
33445     /**
33446      * @event specialkey
33447      * @hide
33448      */
33449     /**
33450      * @cfg {String} fieldClass @hide
33451      */
33452     /**
33453      * @cfg {String} focusClass @hide
33454      */
33455     /**
33456      * @cfg {String} autoCreate @hide
33457      */
33458     /**
33459      * @cfg {String} inputType @hide
33460      */
33461      
33462     /**
33463      * @cfg {String} invalidText @hide
33464      */
33465     /**
33466      * @cfg {String} msgFx @hide
33467      */
33468     /**
33469      * @cfg {String} validateOnBlur @hide
33470      */
33471 });
33472  
33473     
33474    
33475    
33476    
33477       
33478 /**
33479  * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
33480  * @parent Roo.bootstrap.form.HtmlEditor
33481  * @extends Roo.bootstrap.nav.Simplebar
33482  * Basic Toolbar
33483  * 
33484  * @example
33485  * Usage:
33486  *
33487  new Roo.bootstrap.form.HtmlEditor({
33488     ....
33489     toolbars : [
33490         new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
33491             disable : { fonts: 1 , format: 1, ..., ... , ...],
33492             btns : [ .... ]
33493         })
33494     }
33495      
33496  * 
33497  * @cfg {Object} disable List of elements to disable..
33498  * @cfg {Array} btns List of additional buttons.
33499  * 
33500  * 
33501  * NEEDS Extra CSS? 
33502  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
33503  */
33504  
33505 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
33506 {
33507     
33508     Roo.apply(this, config);
33509     
33510     // default disabled, based on 'good practice'..
33511     this.disable = this.disable || {};
33512     Roo.applyIf(this.disable, {
33513         fontSize : true,
33514         colors : true,
33515         specialElements : true
33516     });
33517     Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
33518     
33519     this.editor = config.editor;
33520     this.editorcore = config.editor.editorcore;
33521     
33522     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
33523     
33524     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
33525     // dont call parent... till later.
33526 }
33527 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar,  {
33528      
33529     bar : true,
33530     
33531     editor : false,
33532     editorcore : false,
33533     
33534     
33535     formats : [
33536         "p" ,  
33537         "h1","h2","h3","h4","h5","h6", 
33538         "pre", "code", 
33539         "abbr", "acronym", "address", "cite", "samp", "var",
33540         'div','span'
33541     ],
33542     
33543     
33544     deleteBtn: false,
33545     
33546     onRender : function(ct, position)
33547     {
33548        // Roo.log("Call onRender: " + this.xtype);
33549         
33550        Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
33551        Roo.log(this.el);
33552        this.el.dom.style.marginBottom = '0';
33553        var _this = this;
33554        var editorcore = this.editorcore;
33555        var editor= this.editor;
33556        
33557        var children = [];
33558        var btn = function(id, cmd , toggle, handler, html){
33559        
33560             var  event = toggle ? 'toggle' : 'click';
33561        
33562             var a = {
33563                 size : 'sm',
33564                 xtype: 'Button',
33565                 xns: Roo.bootstrap,
33566                 //glyphicon : id,
33567                 btnid : id,
33568                 fa: id,
33569                 cls : 'roo-html-editor-btn-' + id,
33570                 cmd : cmd, // why id || cmd
33571                 enableToggle: toggle !== false,
33572                 html : html || '',
33573                 pressed : toggle ? false : null,
33574                 listeners : {}
33575             };
33576             a.listeners[toggle ? 'toggle' : 'click'] = function() {
33577                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
33578             };
33579             children.push(a);
33580             return a;
33581        }
33582        
33583     //    var cb_box = function...
33584         
33585         var style = {
33586                 xtype: 'Button',
33587                 size : 'sm',
33588                 xns: Roo.bootstrap,
33589                 fa : 'font',
33590                 cls : 'roo-html-editor-font-chooser',
33591                 //html : 'submit'
33592                 menu : {
33593                     xtype: 'Menu',
33594                     xns: Roo.bootstrap,
33595                     items:  []
33596                 }
33597         };
33598         Roo.each(this.formats, function(f) {
33599             style.menu.items.push({
33600                 xtype :'MenuItem',
33601                 xns: Roo.bootstrap,
33602                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33603                 tagname : f,
33604                 listeners : {
33605                     click : function()
33606                     {
33607                         editorcore.insertTag(this.tagname);
33608                         editor.focus();
33609                     }
33610                 }
33611                 
33612             });
33613         });
33614         children.push(style);   
33615         
33616         btn('bold',         'bold',true);
33617         btn('italic',       'italic',true);
33618         btn('underline',     'underline',true);
33619         btn('align-left',   'justifyleft',true);
33620         btn('align-center', 'justifycenter',true);
33621         btn('align-right' , 'justifyright',true);
33622         btn('link', false, true, this.onLinkClick);
33623         
33624         
33625         btn('image', false, true, this.onImageClick);
33626         btn('list','insertunorderedlist',true);
33627         btn('list-ol','insertorderedlist',true);
33628
33629         btn('pencil', false,true, function(btn){
33630                 Roo.log(this);
33631                 this.toggleSourceEdit(btn.pressed);
33632         });
33633         
33634         if (this.editor.btns.length > 0) {
33635             for (var i = 0; i<this.editor.btns.length; i++) {
33636                 children.push(this.editor.btns[i]);
33637             }
33638         }
33639         
33640         
33641          
33642         this.xtype = 'NavSimplebar'; // why?
33643         
33644         for(var i=0;i< children.length;i++) {
33645             
33646             this.buttons.add(this.addxtypeChild(children[i]));
33647             
33648         }
33649         this.buildToolbarDelete();
33650
33651         editor.on('editorevent', this.updateToolbar, this);
33652     },
33653     
33654     buildToolbarDelete : function()
33655     {
33656         
33657        /* this.addxtypeChild({
33658             xtype : 'Element',
33659             xns : Roo.bootstrap,
33660             cls : 'roo-htmleditor-fill'
33661         });
33662         */
33663         this.deleteBtn = this.addxtypeChild({
33664             size : 'sm',
33665             xtype: 'Button',
33666             xns: Roo.bootstrap,
33667             fa: 'trash',
33668             listeners : {
33669                 click : this.onDelete.createDelegate(this)
33670             }
33671         });
33672         this.deleteBtn.hide();     
33673         
33674     },
33675     
33676     onImageClick : function()
33677     {
33678         if (this.input) {
33679             this.input.un('change', this.onFileSelected, this);
33680         }
33681         this.input = Roo.get(document.body).createChild({ 
33682           tag: 'input', 
33683           type : 'file', 
33684           style : 'display:none', 
33685           multiple: 'multiple'
33686        });
33687         this.input.on('change', this.onFileSelected, this);
33688         this.input.dom.click();
33689     },
33690     
33691     onFileSelected : function(e)
33692     {
33693          e.preventDefault();
33694         
33695         if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33696             return;
33697         }
33698     
33699          
33700         this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33701     },
33702     
33703     addFiles : function(far, fire_add) {
33704
33705          
33706         var editor =  this.editorcore;
33707   
33708         if (!far.length) {
33709             if (fire_add) {
33710                 this.editor.syncValue();
33711                 editor.owner.fireEvent('editorevent', editor.owner, false);
33712                 editor.owner.fireEvent('imageadd', editor.owner, false);
33713             }
33714             return;
33715         }
33716         
33717         var f = far.pop();
33718         
33719         if (!f.type.match(/^image/)) {
33720             this.addFiles(far, fire_add);
33721             return;
33722         }
33723          
33724         var sn = this.selectedNode;
33725         
33726         var bl = sn  && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33727         
33728         
33729         var reader = new FileReader();
33730         reader.addEventListener('load', (function() {
33731             if (bl) {
33732                 var oldSrc = bl.image_src;
33733                 bl.image_src = reader.result;
33734                 //bl.caption = f.name;
33735                 bl.updateElement(sn);
33736                 this.editor.syncValue();
33737                 editor.owner.fireEvent('editorevent', editor.owner, false);
33738                 editor.owner.fireEvent('imageupdate', editor.owner, sn, oldSrc);
33739                 // we only do the first file!! and replace.
33740                 return;
33741             }
33742             if (this.editorcore.enableBlocks) {
33743                 var fig = new Roo.htmleditor.BlockFigure({
33744                     image_src :  reader.result,
33745                     caption : '',
33746                     caption_display : 'none'  //default to hide captions..
33747                  });
33748                 editor.insertAtCursor(fig.toHTML());
33749                 this.addFiles(far, true);
33750                 return;
33751             }
33752             // just a standard img..
33753             if (sn && sn.tagName.toUpperCase() == 'IMG') {
33754                 var oldSrc = sn.src;
33755                 sn.src = reader.result;
33756                 this.editor.syncValue();
33757                 editor.owner.fireEvent('editorevent', editor.owner, false);
33758                 editor.owner.fireEvent('imageupdate', editor.owner, sn, oldSrc);
33759                 return;
33760             }
33761             editor.insertAtCursor('<img src="' + reader.result +'">');
33762             this.addFiles(far, true);
33763             
33764         }).createDelegate(this));
33765         reader.readAsDataURL(f);
33766         
33767     
33768      },
33769     
33770     
33771     onBtnClick : function(id)
33772     {
33773        this.editorcore.relayCmd(id);
33774        this.editorcore.focus();
33775     },
33776     
33777     onLinkClick : function(btn) {
33778         var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33779                 this.selectedNode.getAttribute('href') : '';
33780             
33781         Roo.bootstrap.MessageBox.show({
33782             title : "Add / Edit Link URL",
33783             msg : "Enter the URL for the link",
33784             buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33785             minWidth: 250,
33786             scope : this,
33787             prompt:true,
33788             multiline: false,
33789             modal : true,
33790             value : url,
33791             fn:  function(pressed, newurl) {
33792                 if (pressed != 'ok') {
33793                     this.editorcore.focus();
33794                     return;
33795                 }
33796                 if (url != '') {
33797                     this.selectedNode.setAttribute('href', newurl);
33798                     this.editor.syncValue();
33799                     return;
33800                 }
33801                 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33802                     this.editorcore.relayCmd('createlink', newurl);
33803                 }
33804                 this.editorcore.focus();
33805             },
33806             cls : this.editorcore.linkDialogCls
33807         });
33808     },
33809     /**
33810      * Protected method that will not generally be called directly. It triggers
33811      * a toolbar update by reading the markup state of the current selection in the editor.
33812      */
33813     updateToolbar: function(editor ,ev, sel){
33814
33815         if(!this.editorcore.activated){
33816             this.editor.onFirstFocus(); // is this neeed?
33817             return;
33818         }
33819
33820         var btns = this.buttons; 
33821         var doc = this.editorcore.doc;
33822         var hasToggle  = false;
33823         btns.each(function(e) {
33824             if (e.enableToggle && e.cmd) {
33825                 hasToggle = hasToggle  || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33826                 e.setActive(doc.queryCommandState(e.cmd));
33827             }
33828         }, this);
33829         
33830         
33831         if (ev &&
33832             (ev.type == 'mouseup' || ev.type == 'click' ) &&
33833             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33834             // they have click on an image...
33835             // let's see if we can change the selection...
33836             sel = ev.target;
33837             
33838         }
33839         
33840         var ans = this.editorcore.getAllAncestors();
33841         if (!sel) { 
33842             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
33843             sel = sel ? sel : this.editorcore.doc.body;
33844             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33845             
33846         }
33847         
33848         var lastSel = this.selectedNode;
33849         this.selectedNode = sel;
33850          
33851         // ok see if we are editing a block?
33852         
33853         var db = false;
33854         // you are not actually selecting the block.
33855         if (sel && sel.hasAttribute('data-block')) {
33856             db = sel;
33857         } else if (sel && sel.closest('[data-block]')) {
33858             db = sel.closest('[data-block]');
33859         }
33860         
33861         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33862             e.classList.remove('roo-ed-selection');
33863         });
33864         
33865         var block = false;
33866         if (db && this.editorcore.enableBlocks) {
33867             block = Roo.htmleditor.Block.factory(db);
33868             
33869             if (block) {
33870                 db.className =  (db.classList.length > 0  ? db.className + ' ' : '') +
33871                     ' roo-ed-selection';
33872                 sel = this.selectedNode = db;
33873             }
33874         }
33875         
33876         // highlight the 'a'..
33877         var tn = sel && sel.tagName.toUpperCase() || '';
33878         if (!block && sel && tn != 'A') {
33879             var asel = sel.closest('A');
33880             if (asel) {
33881                 sel = asel;
33882             }
33883         }
33884        
33885         btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33886         btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33887         btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33888         
33889         Roo.bootstrap.menu.Manager.hideAll();
33890          
33891         
33892         
33893         
33894         
33895         // handle delete button..
33896         if (hasToggle || (tn.length && tn == 'BODY')) {
33897             this.deleteBtn.hide();
33898             return;
33899             
33900         }
33901         this.deleteBtn.show();
33902         
33903         
33904         
33905         //this.editorsyncValue();
33906     },
33907     onFirstFocus: function() {
33908         this.buttons.each(function(item){
33909            item.enable();
33910         });
33911     },
33912     
33913     onDelete : function()
33914     {
33915         var range = this.editorcore.createRange();
33916         var selection = this.editorcore.getSelection();
33917         var sn = this.selectedNode;
33918         range.setStart(sn,0);
33919         range.setEnd(sn,0); 
33920         
33921         
33922         if (sn.hasAttribute('data-block')) {
33923             var block = Roo.htmleditor.Block.factory(this.selectedNode);
33924             if (block) {
33925                 sn = block.removeNode();
33926                 sn.parentNode.removeChild(sn);
33927                 selection.removeAllRanges();
33928                 selection.addRange(range);
33929                 this.updateToolbar(null, null, null);
33930                 if (sn.tagName.toUpperCase() == 'FIGURE') {
33931                     this.editor.syncValue();
33932                     this.editor.fireEvent('imagedelete', this.editor, sn);
33933                 }
33934                 
33935                 this.selectedNode = false;
33936                 this.editorcore.fireEditorEvent(false);
33937                 return;
33938             }   
33939              
33940         }
33941         if (!sn) {
33942             return; // should not really happen..
33943         }
33944         if (sn && sn.tagName == 'BODY') {
33945             return;
33946         }
33947         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33948         
33949         // remove and keep parents.
33950         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33951         a.replaceTag(sn);
33952         
33953         selection.removeAllRanges();
33954         selection.addRange(range);
33955         if (sn.tagName.toUpperCase() == 'IMG"') {
33956             this.editor.syncValue();
33957             this.editor.fireEvent('imagedelete', this.editor, sn);
33958         }
33959         
33960         this.selectedNode = false;
33961         this.editorcore.fireEditorEvent(false);
33962         
33963         
33964     },
33965     
33966     
33967     toggleSourceEdit : function(sourceEditMode){
33968         
33969           
33970         if(sourceEditMode){
33971             Roo.log("disabling buttons");
33972            this.buttons.each( function(item){
33973                 if(item.cmd != 'pencil'){
33974                     item.disable();
33975                 }
33976             });
33977           
33978         }else{
33979             Roo.log("enabling buttons");
33980             if(this.editorcore.initialized){
33981                 this.buttons.each( function(item){
33982                     item.enable();
33983                 });
33984             }
33985             
33986         }
33987         Roo.log("calling toggole on editor");
33988         // tell the editor that it's been pressed..
33989         this.editor.toggleSourceEdit(sourceEditMode);
33990        
33991     }
33992 });
33993
33994
33995
33996
33997  
33998 /*
33999  * - LGPL
34000  */
34001
34002 /**
34003  * @class Roo.bootstrap.form.Markdown
34004  * @extends Roo.bootstrap.form.TextArea
34005  * Bootstrap Showdown editable area
34006  * @cfg {string} content
34007  * 
34008  * @constructor
34009  * Create a new Showdown
34010  */
34011
34012 Roo.bootstrap.form.Markdown = function(config){
34013     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
34014    
34015 };
34016
34017 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
34018     
34019     editing :false,
34020     
34021     initEvents : function()
34022     {
34023         
34024         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
34025         this.markdownEl = this.el.createChild({
34026             cls : 'roo-markdown-area'
34027         });
34028         this.inputEl().addClass('d-none');
34029         if (this.getValue() == '') {
34030             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
34031             
34032         } else {
34033             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
34034         }
34035         this.markdownEl.on('click', this.toggleTextEdit, this);
34036         this.on('blur', this.toggleTextEdit, this);
34037         this.on('specialkey', this.resizeTextArea, this);
34038     },
34039     
34040     toggleTextEdit : function()
34041     {
34042         var sh = this.markdownEl.getHeight();
34043         this.inputEl().addClass('d-none');
34044         this.markdownEl.addClass('d-none');
34045         if (!this.editing) {
34046             // show editor?
34047             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
34048             this.inputEl().removeClass('d-none');
34049             this.inputEl().focus();
34050             this.editing = true;
34051             return;
34052         }
34053         // show showdown...
34054         this.updateMarkdown();
34055         this.markdownEl.removeClass('d-none');
34056         this.editing = false;
34057         return;
34058     },
34059     updateMarkdown : function()
34060     {
34061         if (this.getValue() == '') {
34062             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
34063             return;
34064         }
34065  
34066         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
34067     },
34068     
34069     resizeTextArea: function () {
34070         
34071         var sh = 100;
34072         Roo.log([sh, this.getValue().split("\n").length * 30]);
34073         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
34074     },
34075     setValue : function(val)
34076     {
34077         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
34078         if (!this.editing) {
34079             this.updateMarkdown();
34080         }
34081         
34082     },
34083     focus : function()
34084     {
34085         if (!this.editing) {
34086             this.toggleTextEdit();
34087         }
34088         
34089     }
34090
34091
34092 });/*
34093  * Based on:
34094  * Ext JS Library 1.1.1
34095  * Copyright(c) 2006-2007, Ext JS, LLC.
34096  *
34097  * Originally Released Under LGPL - original licence link has changed is not relivant.
34098  *
34099  * Fork - LGPL
34100  * <script type="text/javascript">
34101  */
34102  
34103 /**
34104  * @class Roo.bootstrap.PagingToolbar
34105  * @extends Roo.bootstrap.nav.Simplebar
34106  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
34107  * @constructor
34108  * Create a new PagingToolbar
34109  * @param {Object} config The config object
34110  * @param {Roo.data.Store} store
34111  */
34112 Roo.bootstrap.PagingToolbar = function(config)
34113 {
34114     // old args format still supported... - xtype is prefered..
34115         // created from xtype...
34116     
34117     this.ds = config.dataSource;
34118     
34119     if (config.store && !this.ds) {
34120         this.store= Roo.factory(config.store, Roo.data);
34121         this.ds = this.store;
34122         this.ds.xmodule = this.xmodule || false;
34123     }
34124     
34125     this.toolbarItems = [];
34126     if (config.items) {
34127         this.toolbarItems = config.items;
34128     }
34129     
34130     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
34131     
34132     this.cursor = 0;
34133     
34134     if (this.ds) { 
34135         this.bind(this.ds);
34136     }
34137     
34138     if (Roo.bootstrap.version == 4) {
34139         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
34140     } else {
34141         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
34142     }
34143     
34144 };
34145
34146 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
34147     /**
34148      * @cfg {Roo.bootstrap.Button} buttons[]
34149      * Buttons for the toolbar
34150      */
34151      /**
34152      * @cfg {Roo.data.Store} store
34153      * The underlying data store providing the paged data
34154      */
34155     /**
34156      * @cfg {String/HTMLElement/Element} container
34157      * container The id or element that will contain the toolbar
34158      */
34159     /**
34160      * @cfg {Boolean} displayInfo
34161      * True to display the displayMsg (defaults to false)
34162      */
34163     /**
34164      * @cfg {Number} pageSize
34165      * The number of records to display per page (defaults to 20)
34166      */
34167     pageSize: 20,
34168     /**
34169      * @cfg {String} displayMsg
34170      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
34171      */
34172     displayMsg : 'Displaying {0} - {1} of {2}',
34173     /**
34174      * @cfg {String} emptyMsg
34175      * The message to display when no records are found (defaults to "No data to display")
34176      */
34177     emptyMsg : 'No data to display',
34178     /**
34179      * Customizable piece of the default paging text (defaults to "Page")
34180      * @type String
34181      */
34182     beforePageText : "Page",
34183     /**
34184      * Customizable piece of the default paging text (defaults to "of %0")
34185      * @type String
34186      */
34187     afterPageText : "of {0}",
34188     /**
34189      * Customizable piece of the default paging text (defaults to "First Page")
34190      * @type String
34191      */
34192     firstText : "First Page",
34193     /**
34194      * Customizable piece of the default paging text (defaults to "Previous Page")
34195      * @type String
34196      */
34197     prevText : "Previous Page",
34198     /**
34199      * Customizable piece of the default paging text (defaults to "Next Page")
34200      * @type String
34201      */
34202     nextText : "Next Page",
34203     /**
34204      * Customizable piece of the default paging text (defaults to "Last Page")
34205      * @type String
34206      */
34207     lastText : "Last Page",
34208     /**
34209      * Customizable piece of the default paging text (defaults to "Refresh")
34210      * @type String
34211      */
34212     refreshText : "Refresh",
34213
34214     buttons : false,
34215     // private
34216     onRender : function(ct, position) 
34217     {
34218         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
34219         this.navgroup.parentId = this.id;
34220         this.navgroup.onRender(this.el, null);
34221         // add the buttons to the navgroup
34222         
34223         if(this.displayInfo){
34224             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
34225             this.displayEl = this.el.select('.x-paging-info', true).first();
34226 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
34227 //            this.displayEl = navel.el.select('span',true).first();
34228         }
34229         
34230         var _this = this;
34231         
34232         if(this.buttons){
34233             Roo.each(_this.buttons, function(e){ // this might need to use render????
34234                Roo.factory(e).render(_this.el);
34235             });
34236         }
34237             
34238         Roo.each(_this.toolbarItems, function(e) {
34239             _this.navgroup.addItem(e);
34240         });
34241         
34242         
34243         this.first = this.navgroup.addItem({
34244             tooltip: this.firstText,
34245             cls: "prev btn-outline-secondary",
34246             html : ' <i class="fa fa-step-backward"></i>',
34247             disabled: true,
34248             preventDefault: true,
34249             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
34250         });
34251         
34252         this.prev =  this.navgroup.addItem({
34253             tooltip: this.prevText,
34254             cls: "prev btn-outline-secondary",
34255             html : ' <i class="fa fa-backward"></i>',
34256             disabled: true,
34257             preventDefault: true,
34258             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
34259         });
34260     //this.addSeparator();
34261         
34262         
34263         var field = this.navgroup.addItem( {
34264             tagtype : 'span',
34265             cls : 'x-paging-position  btn-outline-secondary',
34266              disabled: true,
34267             html : this.beforePageText  +
34268                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
34269                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
34270          } ); //?? escaped?
34271         
34272         this.field = field.el.select('input', true).first();
34273         this.field.on("keydown", this.onPagingKeydown, this);
34274         this.field.on("focus", function(){this.dom.select();});
34275     
34276     
34277         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
34278         //this.field.setHeight(18);
34279         //this.addSeparator();
34280         this.next = this.navgroup.addItem({
34281             tooltip: this.nextText,
34282             cls: "next btn-outline-secondary",
34283             html : ' <i class="fa fa-forward"></i>',
34284             disabled: true,
34285             preventDefault: true,
34286             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
34287         });
34288         this.last = this.navgroup.addItem({
34289             tooltip: this.lastText,
34290             html : ' <i class="fa fa-step-forward"></i>',
34291             cls: "next btn-outline-secondary",
34292             disabled: true,
34293             preventDefault: true,
34294             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
34295         });
34296     //this.addSeparator();
34297         this.loading = this.navgroup.addItem({
34298             tooltip: this.refreshText,
34299             cls: "btn-outline-secondary",
34300             html : ' <i class="fa fa-refresh"></i>',
34301             preventDefault: true,
34302             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
34303         });
34304         
34305     },
34306
34307     // private
34308     updateInfo : function(){
34309         if(this.displayEl){
34310             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
34311             var msg = count == 0 ?
34312                 this.emptyMsg :
34313                 String.format(
34314                     this.displayMsg,
34315                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
34316                 );
34317             this.displayEl.update(msg);
34318         }
34319     },
34320
34321     // private
34322     onLoad : function(ds, r, o)
34323     {
34324         this.cursor = o.params && o.params.start ? o.params.start : 0;
34325         
34326         var d = this.getPageData(),
34327             ap = d.activePage,
34328             ps = d.pages;
34329         
34330         
34331         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
34332         this.field.dom.value = ap;
34333         this.first.setDisabled(ap == 1);
34334         this.prev.setDisabled(ap == 1);
34335         this.next.setDisabled(ap == ps);
34336         this.last.setDisabled(ap == ps);
34337         this.loading.enable();
34338         this.updateInfo();
34339     },
34340
34341     // private
34342     getPageData : function(){
34343         var total = this.ds.getTotalCount();
34344         return {
34345             total : total,
34346             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
34347             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
34348         };
34349     },
34350
34351     // private
34352     onLoadError : function(proxy, o){
34353         this.loading.enable();
34354         if (this.ds.events.loadexception.listeners.length  < 2) {
34355             // nothing has been assigned to loadexception except this...
34356             // so 
34357             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
34358
34359         }
34360     },
34361
34362     // private
34363     onPagingKeydown : function(e){
34364         var k = e.getKey();
34365         var d = this.getPageData();
34366         if(k == e.RETURN){
34367             var v = this.field.dom.value, pageNum;
34368             if(!v || isNaN(pageNum = parseInt(v, 10))){
34369                 this.field.dom.value = d.activePage;
34370                 return;
34371             }
34372             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
34373             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34374             e.stopEvent();
34375         }
34376         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))
34377         {
34378           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
34379           this.field.dom.value = pageNum;
34380           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
34381           e.stopEvent();
34382         }
34383         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
34384         {
34385           var v = this.field.dom.value, pageNum; 
34386           var increment = (e.shiftKey) ? 10 : 1;
34387           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
34388                 increment *= -1;
34389           }
34390           if(!v || isNaN(pageNum = parseInt(v, 10))) {
34391             this.field.dom.value = d.activePage;
34392             return;
34393           }
34394           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
34395           {
34396             this.field.dom.value = parseInt(v, 10) + increment;
34397             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
34398             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34399           }
34400           e.stopEvent();
34401         }
34402     },
34403
34404     // private
34405     beforeLoad : function(){
34406         if(this.loading){
34407             this.loading.disable();
34408         }
34409     },
34410
34411     // private
34412     onClick : function(which){
34413         
34414         var ds = this.ds;
34415         if (!ds) {
34416             return;
34417         }
34418         
34419         switch(which){
34420             case "first":
34421                 ds.load({params:{start: 0, limit: this.pageSize}});
34422             break;
34423             case "prev":
34424                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
34425             break;
34426             case "next":
34427                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
34428             break;
34429             case "last":
34430                 var total = ds.getTotalCount();
34431                 var extra = total % this.pageSize;
34432                 var lastStart = extra ? (total - extra) : total-this.pageSize;
34433                 ds.load({params:{start: lastStart, limit: this.pageSize}});
34434             break;
34435             case "refresh":
34436                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
34437             break;
34438         }
34439     },
34440
34441     /**
34442      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
34443      * @param {Roo.data.Store} store The data store to unbind
34444      */
34445     unbind : function(ds){
34446         ds.un("beforeload", this.beforeLoad, this);
34447         ds.un("load", this.onLoad, this);
34448         ds.un("loadexception", this.onLoadError, this);
34449         ds.un("remove", this.updateInfo, this);
34450         ds.un("add", this.updateInfo, this);
34451         this.ds = undefined;
34452     },
34453
34454     /**
34455      * Binds the paging toolbar to the specified {@link Roo.data.Store}
34456      * @param {Roo.data.Store} store The data store to bind
34457      */
34458     bind : function(ds){
34459         ds.on("beforeload", this.beforeLoad, this);
34460         ds.on("load", this.onLoad, this);
34461         ds.on("loadexception", this.onLoadError, this);
34462         ds.on("remove", this.updateInfo, this);
34463         ds.on("add", this.updateInfo, this);
34464         this.ds = ds;
34465     }
34466 });/*
34467  * - LGPL
34468  *
34469  * element
34470  * 
34471  */
34472
34473 /**
34474  * @class Roo.bootstrap.MessageBar
34475  * @extends Roo.bootstrap.Component
34476  * Bootstrap MessageBar class
34477  * @cfg {String} html contents of the MessageBar
34478  * @cfg {String} weight (info | success | warning | danger) default info
34479  * @cfg {String} beforeClass insert the bar before the given class
34480  * @cfg {Boolean} closable (true | false) default false
34481  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
34482  * 
34483  * @constructor
34484  * Create a new Element
34485  * @param {Object} config The config object
34486  */
34487
34488 Roo.bootstrap.MessageBar = function(config){
34489     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
34490 };
34491
34492 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
34493     
34494     html: '',
34495     weight: 'info',
34496     closable: false,
34497     fixed: false,
34498     beforeClass: 'bootstrap-sticky-wrap',
34499     
34500     getAutoCreate : function(){
34501         
34502         var cfg = {
34503             tag: 'div',
34504             cls: 'alert alert-dismissable alert-' + this.weight,
34505             cn: [
34506                 {
34507                     tag: 'span',
34508                     cls: 'message',
34509                     html: this.html || ''
34510                 }
34511             ]
34512         };
34513         
34514         if(this.fixed){
34515             cfg.cls += ' alert-messages-fixed';
34516         }
34517         
34518         if(this.closable){
34519             cfg.cn.push({
34520                 tag: 'button',
34521                 cls: 'close',
34522                 html: 'x'
34523             });
34524         }
34525         
34526         return cfg;
34527     },
34528     
34529     onRender : function(ct, position)
34530     {
34531         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
34532         
34533         if(!this.el){
34534             var cfg = Roo.apply({},  this.getAutoCreate());
34535             cfg.id = Roo.id();
34536             
34537             if (this.cls) {
34538                 cfg.cls += ' ' + this.cls;
34539             }
34540             if (this.style) {
34541                 cfg.style = this.style;
34542             }
34543             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
34544             
34545             this.el.setVisibilityMode(Roo.Element.DISPLAY);
34546         }
34547         
34548         this.el.select('>button.close').on('click', this.hide, this);
34549         
34550     },
34551     
34552     show : function()
34553     {
34554         if (!this.rendered) {
34555             this.render();
34556         }
34557         
34558         this.el.show();
34559         
34560         this.fireEvent('show', this);
34561         
34562     },
34563     
34564     hide : function()
34565     {
34566         if (!this.rendered) {
34567             this.render();
34568         }
34569         
34570         this.el.hide();
34571         
34572         this.fireEvent('hide', this);
34573     },
34574     
34575     update : function()
34576     {
34577 //        var e = this.el.dom.firstChild;
34578 //        
34579 //        if(this.closable){
34580 //            e = e.nextSibling;
34581 //        }
34582 //        
34583 //        e.data = this.html || '';
34584
34585         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34586     }
34587    
34588 });
34589
34590  
34591
34592      /*
34593  * - LGPL
34594  *
34595  * Graph
34596  * 
34597  */
34598
34599
34600 /**
34601  * @class Roo.bootstrap.Graph
34602  * @extends Roo.bootstrap.Component
34603  * Bootstrap Graph class
34604 > Prameters
34605  -sm {number} sm 4
34606  -md {number} md 5
34607  @cfg {String} graphtype  bar | vbar | pie
34608  @cfg {number} g_x coodinator | centre x (pie)
34609  @cfg {number} g_y coodinator | centre y (pie)
34610  @cfg {number} g_r radius (pie)
34611  @cfg {number} g_height height of the chart (respected by all elements in the set)
34612  @cfg {number} g_width width of the chart (respected by all elements in the set)
34613  @cfg {Object} title The title of the chart
34614     
34615  -{Array}  values
34616  -opts (object) options for the chart 
34617      o {
34618      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34619      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34620      o vgutter (number)
34621      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.
34622      o stacked (boolean) whether or not to tread values as in a stacked bar chart
34623      o to
34624      o stretch (boolean)
34625      o }
34626  -opts (object) options for the pie
34627      o{
34628      o cut
34629      o startAngle (number)
34630      o endAngle (number)
34631      } 
34632  *
34633  * @constructor
34634  * Create a new Input
34635  * @param {Object} config The config object
34636  */
34637
34638 Roo.bootstrap.Graph = function(config){
34639     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34640     
34641     this.addEvents({
34642         // img events
34643         /**
34644          * @event click
34645          * The img click event for the img.
34646          * @param {Roo.EventObject} e
34647          */
34648         "click" : true
34649     });
34650 };
34651
34652 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
34653     
34654     sm: 4,
34655     md: 5,
34656     graphtype: 'bar',
34657     g_height: 250,
34658     g_width: 400,
34659     g_x: 50,
34660     g_y: 50,
34661     g_r: 30,
34662     opts:{
34663         //g_colors: this.colors,
34664         g_type: 'soft',
34665         g_gutter: '20%'
34666
34667     },
34668     title : false,
34669
34670     getAutoCreate : function(){
34671         
34672         var cfg = {
34673             tag: 'div',
34674             html : null
34675         };
34676         
34677         
34678         return  cfg;
34679     },
34680
34681     onRender : function(ct,position){
34682         
34683         
34684         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34685         
34686         if (typeof(Raphael) == 'undefined') {
34687             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34688             return;
34689         }
34690         
34691         this.raphael = Raphael(this.el.dom);
34692         
34693                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34694                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34695                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34696                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34697                 /*
34698                 r.text(160, 10, "Single Series Chart").attr(txtattr);
34699                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34700                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34701                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34702                 
34703                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34704                 r.barchart(330, 10, 300, 220, data1);
34705                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34706                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34707                 */
34708                 
34709                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34710                 // r.barchart(30, 30, 560, 250,  xdata, {
34711                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34712                 //     axis : "0 0 1 1",
34713                 //     axisxlabels :  xdata
34714                 //     //yvalues : cols,
34715                    
34716                 // });
34717 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34718 //        
34719 //        this.load(null,xdata,{
34720 //                axis : "0 0 1 1",
34721 //                axisxlabels :  xdata
34722 //                });
34723
34724     },
34725
34726     load : function(graphtype,xdata,opts)
34727     {
34728         this.raphael.clear();
34729         if(!graphtype) {
34730             graphtype = this.graphtype;
34731         }
34732         if(!opts){
34733             opts = this.opts;
34734         }
34735         var r = this.raphael,
34736             fin = function () {
34737                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34738             },
34739             fout = function () {
34740                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34741             },
34742             pfin = function() {
34743                 this.sector.stop();
34744                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34745
34746                 if (this.label) {
34747                     this.label[0].stop();
34748                     this.label[0].attr({ r: 7.5 });
34749                     this.label[1].attr({ "font-weight": 800 });
34750                 }
34751             },
34752             pfout = function() {
34753                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34754
34755                 if (this.label) {
34756                     this.label[0].animate({ r: 5 }, 500, "bounce");
34757                     this.label[1].attr({ "font-weight": 400 });
34758                 }
34759             };
34760
34761         switch(graphtype){
34762             case 'bar':
34763                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34764                 break;
34765             case 'hbar':
34766                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34767                 break;
34768             case 'pie':
34769 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
34770 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34771 //            
34772                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34773                 
34774                 break;
34775
34776         }
34777         
34778         if(this.title){
34779             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34780         }
34781         
34782     },
34783     
34784     setTitle: function(o)
34785     {
34786         this.title = o;
34787     },
34788     
34789     initEvents: function() {
34790         
34791         if(!this.href){
34792             this.el.on('click', this.onClick, this);
34793         }
34794     },
34795     
34796     onClick : function(e)
34797     {
34798         Roo.log('img onclick');
34799         this.fireEvent('click', this, e);
34800     }
34801    
34802 });
34803
34804  
34805 Roo.bootstrap.dash = {};/*
34806  * - LGPL
34807  *
34808  * numberBox
34809  * 
34810  */
34811 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34812
34813 /**
34814  * @class Roo.bootstrap.dash.NumberBox
34815  * @extends Roo.bootstrap.Component
34816  * Bootstrap NumberBox class
34817  * @cfg {String} headline Box headline
34818  * @cfg {String} content Box content
34819  * @cfg {String} icon Box icon
34820  * @cfg {String} footer Footer text
34821  * @cfg {String} fhref Footer href
34822  * 
34823  * @constructor
34824  * Create a new NumberBox
34825  * @param {Object} config The config object
34826  */
34827
34828
34829 Roo.bootstrap.dash.NumberBox = function(config){
34830     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34831     
34832 };
34833
34834 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
34835     
34836     headline : '',
34837     content : '',
34838     icon : '',
34839     footer : '',
34840     fhref : '',
34841     ficon : '',
34842     
34843     getAutoCreate : function(){
34844         
34845         var cfg = {
34846             tag : 'div',
34847             cls : 'small-box ',
34848             cn : [
34849                 {
34850                     tag : 'div',
34851                     cls : 'inner',
34852                     cn :[
34853                         {
34854                             tag : 'h3',
34855                             cls : 'roo-headline',
34856                             html : this.headline
34857                         },
34858                         {
34859                             tag : 'p',
34860                             cls : 'roo-content',
34861                             html : this.content
34862                         }
34863                     ]
34864                 }
34865             ]
34866         };
34867         
34868         if(this.icon){
34869             cfg.cn.push({
34870                 tag : 'div',
34871                 cls : 'icon',
34872                 cn :[
34873                     {
34874                         tag : 'i',
34875                         cls : 'ion ' + this.icon
34876                     }
34877                 ]
34878             });
34879         }
34880         
34881         if(this.footer){
34882             var footer = {
34883                 tag : 'a',
34884                 cls : 'small-box-footer',
34885                 href : this.fhref || '#',
34886                 html : this.footer
34887             };
34888             
34889             cfg.cn.push(footer);
34890             
34891         }
34892         
34893         return  cfg;
34894     },
34895
34896     onRender : function(ct,position){
34897         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34898
34899
34900        
34901                 
34902     },
34903
34904     setHeadline: function (value)
34905     {
34906         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34907     },
34908     
34909     setFooter: function (value, href)
34910     {
34911         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34912         
34913         if(href){
34914             this.el.select('a.small-box-footer',true).first().attr('href', href);
34915         }
34916         
34917     },
34918
34919     setContent: function (value)
34920     {
34921         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34922     },
34923
34924     initEvents: function() 
34925     {   
34926         
34927     }
34928     
34929 });
34930
34931  
34932 /*
34933  * - LGPL
34934  *
34935  * TabBox
34936  * 
34937  */
34938 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34939
34940 /**
34941  * @class Roo.bootstrap.dash.TabBox
34942  * @extends Roo.bootstrap.Component
34943  * @children Roo.bootstrap.dash.TabPane
34944  * Bootstrap TabBox class
34945  * @cfg {String} title Title of the TabBox
34946  * @cfg {String} icon Icon of the TabBox
34947  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34948  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34949  * 
34950  * @constructor
34951  * Create a new TabBox
34952  * @param {Object} config The config object
34953  */
34954
34955
34956 Roo.bootstrap.dash.TabBox = function(config){
34957     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34958     this.addEvents({
34959         // raw events
34960         /**
34961          * @event addpane
34962          * When a pane is added
34963          * @param {Roo.bootstrap.dash.TabPane} pane
34964          */
34965         "addpane" : true,
34966         /**
34967          * @event activatepane
34968          * When a pane is activated
34969          * @param {Roo.bootstrap.dash.TabPane} pane
34970          */
34971         "activatepane" : true
34972         
34973          
34974     });
34975     
34976     this.panes = [];
34977 };
34978
34979 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34980
34981     title : '',
34982     icon : false,
34983     showtabs : true,
34984     tabScrollable : false,
34985     
34986     getChildContainer : function()
34987     {
34988         return this.el.select('.tab-content', true).first();
34989     },
34990     
34991     getAutoCreate : function(){
34992         
34993         var header = {
34994             tag: 'li',
34995             cls: 'pull-left header',
34996             html: this.title,
34997             cn : []
34998         };
34999         
35000         if(this.icon){
35001             header.cn.push({
35002                 tag: 'i',
35003                 cls: 'fa ' + this.icon
35004             });
35005         }
35006         
35007         var h = {
35008             tag: 'ul',
35009             cls: 'nav nav-tabs pull-right',
35010             cn: [
35011                 header
35012             ]
35013         };
35014         
35015         if(this.tabScrollable){
35016             h = {
35017                 tag: 'div',
35018                 cls: 'tab-header',
35019                 cn: [
35020                     {
35021                         tag: 'ul',
35022                         cls: 'nav nav-tabs pull-right',
35023                         cn: [
35024                             header
35025                         ]
35026                     }
35027                 ]
35028             };
35029         }
35030         
35031         var cfg = {
35032             tag: 'div',
35033             cls: 'nav-tabs-custom',
35034             cn: [
35035                 h,
35036                 {
35037                     tag: 'div',
35038                     cls: 'tab-content no-padding',
35039                     cn: []
35040                 }
35041             ]
35042         };
35043
35044         return  cfg;
35045     },
35046     initEvents : function()
35047     {
35048         //Roo.log('add add pane handler');
35049         this.on('addpane', this.onAddPane, this);
35050     },
35051      /**
35052      * Updates the box title
35053      * @param {String} html to set the title to.
35054      */
35055     setTitle : function(value)
35056     {
35057         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
35058     },
35059     onAddPane : function(pane)
35060     {
35061         this.panes.push(pane);
35062         //Roo.log('addpane');
35063         //Roo.log(pane);
35064         // tabs are rendere left to right..
35065         if(!this.showtabs){
35066             return;
35067         }
35068         
35069         var ctr = this.el.select('.nav-tabs', true).first();
35070          
35071          
35072         var existing = ctr.select('.nav-tab',true);
35073         var qty = existing.getCount();;
35074         
35075         
35076         var tab = ctr.createChild({
35077             tag : 'li',
35078             cls : 'nav-tab' + (qty ? '' : ' active'),
35079             cn : [
35080                 {
35081                     tag : 'a',
35082                     href:'#',
35083                     html : pane.title
35084                 }
35085             ]
35086         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
35087         pane.tab = tab;
35088         
35089         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
35090         if (!qty) {
35091             pane.el.addClass('active');
35092         }
35093         
35094                 
35095     },
35096     onTabClick : function(ev,un,ob,pane)
35097     {
35098         //Roo.log('tab - prev default');
35099         ev.preventDefault();
35100         
35101         
35102         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
35103         pane.tab.addClass('active');
35104         //Roo.log(pane.title);
35105         this.getChildContainer().select('.tab-pane',true).removeClass('active');
35106         // technically we should have a deactivate event.. but maybe add later.
35107         // and it should not de-activate the selected tab...
35108         this.fireEvent('activatepane', pane);
35109         pane.el.addClass('active');
35110         pane.fireEvent('activate');
35111         
35112         
35113     },
35114     
35115     getActivePane : function()
35116     {
35117         var r = false;
35118         Roo.each(this.panes, function(p) {
35119             if(p.el.hasClass('active')){
35120                 r = p;
35121                 return false;
35122             }
35123             
35124             return;
35125         });
35126         
35127         return r;
35128     }
35129     
35130     
35131 });
35132
35133  
35134 /*
35135  * - LGPL
35136  *
35137  * Tab pane
35138  * 
35139  */
35140 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
35141 /**
35142  * @class Roo.bootstrap.TabPane
35143  * @extends Roo.bootstrap.Component
35144  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
35145  * Bootstrap TabPane class
35146  * @cfg {Boolean} active (false | true) Default false
35147  * @cfg {String} title title of panel
35148
35149  * 
35150  * @constructor
35151  * Create a new TabPane
35152  * @param {Object} config The config object
35153  */
35154
35155 Roo.bootstrap.dash.TabPane = function(config){
35156     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
35157     
35158     this.addEvents({
35159         // raw events
35160         /**
35161          * @event activate
35162          * When a pane is activated
35163          * @param {Roo.bootstrap.dash.TabPane} pane
35164          */
35165         "activate" : true
35166          
35167     });
35168 };
35169
35170 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
35171     
35172     active : false,
35173     title : '',
35174     
35175     // the tabBox that this is attached to.
35176     tab : false,
35177      
35178     getAutoCreate : function() 
35179     {
35180         var cfg = {
35181             tag: 'div',
35182             cls: 'tab-pane'
35183         };
35184         
35185         if(this.active){
35186             cfg.cls += ' active';
35187         }
35188         
35189         return cfg;
35190     },
35191     initEvents  : function()
35192     {
35193         //Roo.log('trigger add pane handler');
35194         this.parent().fireEvent('addpane', this)
35195     },
35196     
35197      /**
35198      * Updates the tab title 
35199      * @param {String} html to set the title to.
35200      */
35201     setTitle: function(str)
35202     {
35203         if (!this.tab) {
35204             return;
35205         }
35206         this.title = str;
35207         this.tab.select('a', true).first().dom.innerHTML = str;
35208         
35209     }
35210     
35211     
35212     
35213 });
35214
35215  
35216
35217
35218  /*
35219  * - LGPL
35220  *
35221  * Tooltip
35222  * 
35223  */
35224
35225 /**
35226  * @class Roo.bootstrap.Tooltip
35227  * Bootstrap Tooltip class
35228  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
35229  * to determine which dom element triggers the tooltip.
35230  * 
35231  * It needs to add support for additional attributes like tooltip-position
35232  * 
35233  * @constructor
35234  * Create a new Toolti
35235  * @param {Object} config The config object
35236  */
35237
35238 Roo.bootstrap.Tooltip = function(config){
35239     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
35240     
35241     this.alignment = Roo.bootstrap.Tooltip.alignment;
35242     
35243     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
35244         this.alignment = config.alignment;
35245     }
35246     
35247 };
35248
35249 Roo.apply(Roo.bootstrap.Tooltip, {
35250     /**
35251      * @function init initialize tooltip monitoring.
35252      * @static
35253      */
35254     currentEl : false,
35255     currentTip : false,
35256     currentRegion : false,
35257     
35258     //  init : delay?
35259     
35260     init : function()
35261     {
35262         Roo.get(document).on('mouseover', this.enter ,this);
35263         Roo.get(document).on('mouseout', this.leave, this);
35264          
35265         
35266         this.currentTip = new Roo.bootstrap.Tooltip();
35267     },
35268     
35269     enter : function(ev)
35270     {
35271         var dom = ev.getTarget();
35272         
35273         //Roo.log(['enter',dom]);
35274         var el = Roo.fly(dom);
35275         if (this.currentEl) {
35276             //Roo.log(dom);
35277             //Roo.log(this.currentEl);
35278             //Roo.log(this.currentEl.contains(dom));
35279             if (this.currentEl == el) {
35280                 return;
35281             }
35282             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
35283                 return;
35284             }
35285
35286         }
35287         
35288         if (this.currentTip.el) {
35289             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
35290         }    
35291         //Roo.log(ev);
35292         
35293         if(!el || el.dom == document){
35294             return;
35295         }
35296         
35297         var bindEl = el; 
35298         var pel = false;
35299         if (!el.attr('tooltip')) {
35300             pel = el.findParent("[tooltip]");
35301             if (pel) {
35302                 bindEl = Roo.get(pel);
35303             }
35304         }
35305         
35306        
35307         
35308         // you can not look for children, as if el is the body.. then everythign is the child..
35309         if (!pel && !el.attr('tooltip')) { //
35310             if (!el.select("[tooltip]").elements.length) {
35311                 return;
35312             }
35313             // is the mouse over this child...?
35314             bindEl = el.select("[tooltip]").first();
35315             var xy = ev.getXY();
35316             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
35317                 //Roo.log("not in region.");
35318                 return;
35319             }
35320             //Roo.log("child element over..");
35321             
35322         }
35323         this.currentEl = el;
35324         this.currentTip.bind(bindEl);
35325         this.currentRegion = Roo.lib.Region.getRegion(dom);
35326         this.currentTip.enter();
35327         
35328     },
35329     leave : function(ev)
35330     {
35331         var dom = ev.getTarget();
35332         //Roo.log(['leave',dom]);
35333         if (!this.currentEl) {
35334             return;
35335         }
35336         
35337         
35338         if (dom != this.currentEl.dom) {
35339             return;
35340         }
35341         var xy = ev.getXY();
35342         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
35343             return;
35344         }
35345         // only activate leave if mouse cursor is outside... bounding box..
35346         
35347         
35348         
35349         
35350         if (this.currentTip) {
35351             this.currentTip.leave();
35352         }
35353         //Roo.log('clear currentEl');
35354         this.currentEl = false;
35355         
35356         
35357     },
35358     alignment : {
35359         'left' : ['r-l', [-2,0], 'right'],
35360         'right' : ['l-r', [2,0], 'left'],
35361         'bottom' : ['t-b', [0,2], 'top'],
35362         'top' : [ 'b-t', [0,-2], 'bottom']
35363     }
35364     
35365 });
35366
35367
35368 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
35369     
35370     
35371     bindEl : false,
35372     
35373     delay : null, // can be { show : 300 , hide: 500}
35374     
35375     timeout : null,
35376     
35377     hoverState : null, //???
35378     
35379     placement : 'bottom', 
35380     
35381     alignment : false,
35382     
35383     getAutoCreate : function(){
35384     
35385         var cfg = {
35386            cls : 'tooltip',   
35387            role : 'tooltip',
35388            cn : [
35389                 {
35390                     cls : 'tooltip-arrow arrow'
35391                 },
35392                 {
35393                     cls : 'tooltip-inner'
35394                 }
35395            ]
35396         };
35397         
35398         return cfg;
35399     },
35400     bind : function(el)
35401     {
35402         this.bindEl = el;
35403     },
35404     
35405     initEvents : function()
35406     {
35407         this.arrowEl = this.el.select('.arrow', true).first();
35408         this.innerEl = this.el.select('.tooltip-inner', true).first();
35409     },
35410     
35411     enter : function () {
35412        
35413         if (this.timeout != null) {
35414             clearTimeout(this.timeout);
35415         }
35416         
35417         this.hoverState = 'in';
35418          //Roo.log("enter - show");
35419         if (!this.delay || !this.delay.show) {
35420             this.show();
35421             return;
35422         }
35423         var _t = this;
35424         this.timeout = setTimeout(function () {
35425             if (_t.hoverState == 'in') {
35426                 _t.show();
35427             }
35428         }, this.delay.show);
35429     },
35430     leave : function()
35431     {
35432         clearTimeout(this.timeout);
35433     
35434         this.hoverState = 'out';
35435          if (!this.delay || !this.delay.hide) {
35436             this.hide();
35437             return;
35438         }
35439        
35440         var _t = this;
35441         this.timeout = setTimeout(function () {
35442             //Roo.log("leave - timeout");
35443             
35444             if (_t.hoverState == 'out') {
35445                 _t.hide();
35446                 Roo.bootstrap.Tooltip.currentEl = false;
35447             }
35448         }, delay);
35449     },
35450     
35451     show : function (msg)
35452     {
35453         if (!this.el) {
35454             this.render(document.body);
35455         }
35456         // set content.
35457         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
35458         
35459         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
35460         
35461         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
35462         
35463         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
35464                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
35465
35466         if(this.bindEl.attr('tooltip-class')) {
35467             this.el.addClass(this.bindEl.attr('tooltip-class'));
35468         }
35469         
35470         var placement = typeof this.placement == 'function' ?
35471             this.placement.call(this, this.el, on_el) :
35472             this.placement;
35473         
35474         if(this.bindEl.attr('tooltip-placement')) {
35475             placement = this.bindEl.attr('tooltip-placement');
35476         }
35477             
35478         var autoToken = /\s?auto?\s?/i;
35479         var autoPlace = autoToken.test(placement);
35480         if (autoPlace) {
35481             placement = placement.replace(autoToken, '') || 'top';
35482         }
35483         
35484         //this.el.detach()
35485         //this.el.setXY([0,0]);
35486         this.el.show();
35487         //this.el.dom.style.display='block';
35488         
35489         //this.el.appendTo(on_el);
35490         
35491         var p = this.getPosition();
35492         var box = this.el.getBox();
35493         
35494         if (autoPlace) {
35495             // fixme..
35496         }
35497         
35498         var align = this.alignment[placement];
35499         
35500         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
35501         
35502         if(placement == 'top' || placement == 'bottom'){
35503             if(xy[0] < 0){
35504                 placement = 'right';
35505             }
35506             
35507             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
35508                 placement = 'left';
35509             }
35510             
35511             var scroll = Roo.select('body', true).first().getScroll();
35512             
35513             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
35514                 placement = 'top';
35515             }
35516             
35517             align = this.alignment[placement];
35518             
35519             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
35520             
35521         }
35522         
35523         var elems = document.getElementsByTagName('div');
35524         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
35525         for (var i = 0; i < elems.length; i++) {
35526           var zindex = Number.parseInt(
35527                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
35528                 10
35529           );
35530           if (zindex > highest) {
35531             highest = zindex;
35532           }
35533         }
35534         
35535         
35536         
35537         this.el.dom.style.zIndex = highest;
35538         
35539         this.el.alignTo(this.bindEl, align[0],align[1]);
35540         //var arrow = this.el.select('.arrow',true).first();
35541         //arrow.set(align[2], 
35542         
35543         this.el.addClass(placement);
35544         this.el.addClass("bs-tooltip-"+ placement);
35545         
35546         this.el.addClass('in fade show');
35547         
35548         this.hoverState = null;
35549         
35550         if (this.el.hasClass('fade')) {
35551             // fade it?
35552         }
35553         
35554         
35555         
35556         
35557         
35558     },
35559     hide : function()
35560     {
35561          
35562         if (!this.el) {
35563             return;
35564         }
35565         //this.el.setXY([0,0]);
35566         if(this.bindEl.attr('tooltip-class')) {
35567             this.el.removeClass(this.bindEl.attr('tooltip-class'));
35568         }
35569         this.el.removeClass(['show', 'in']);
35570         //this.el.hide();
35571         
35572     }
35573     
35574 });
35575  
35576
35577  /*
35578  * - LGPL
35579  *
35580  * Location Picker
35581  * 
35582  */
35583
35584 /**
35585  * @class Roo.bootstrap.LocationPicker
35586  * @extends Roo.bootstrap.Component
35587  * Bootstrap LocationPicker class
35588  * @cfg {Number} latitude Position when init default 0
35589  * @cfg {Number} longitude Position when init default 0
35590  * @cfg {Number} zoom default 15
35591  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35592  * @cfg {Boolean} mapTypeControl default false
35593  * @cfg {Boolean} disableDoubleClickZoom default false
35594  * @cfg {Boolean} scrollwheel default true
35595  * @cfg {Boolean} streetViewControl default false
35596  * @cfg {Number} radius default 0
35597  * @cfg {String} locationName
35598  * @cfg {Boolean} draggable default true
35599  * @cfg {Boolean} enableAutocomplete default false
35600  * @cfg {Boolean} enableReverseGeocode default true
35601  * @cfg {String} markerTitle
35602  * 
35603  * @constructor
35604  * Create a new LocationPicker
35605  * @param {Object} config The config object
35606  */
35607
35608
35609 Roo.bootstrap.LocationPicker = function(config){
35610     
35611     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35612     
35613     this.addEvents({
35614         /**
35615          * @event initial
35616          * Fires when the picker initialized.
35617          * @param {Roo.bootstrap.LocationPicker} this
35618          * @param {Google Location} location
35619          */
35620         initial : true,
35621         /**
35622          * @event positionchanged
35623          * Fires when the picker position changed.
35624          * @param {Roo.bootstrap.LocationPicker} this
35625          * @param {Google Location} location
35626          */
35627         positionchanged : true,
35628         /**
35629          * @event resize
35630          * Fires when the map resize.
35631          * @param {Roo.bootstrap.LocationPicker} this
35632          */
35633         resize : true,
35634         /**
35635          * @event show
35636          * Fires when the map show.
35637          * @param {Roo.bootstrap.LocationPicker} this
35638          */
35639         show : true,
35640         /**
35641          * @event hide
35642          * Fires when the map hide.
35643          * @param {Roo.bootstrap.LocationPicker} this
35644          */
35645         hide : true,
35646         /**
35647          * @event mapClick
35648          * Fires when click the map.
35649          * @param {Roo.bootstrap.LocationPicker} this
35650          * @param {Map event} e
35651          */
35652         mapClick : true,
35653         /**
35654          * @event mapRightClick
35655          * Fires when right click the map.
35656          * @param {Roo.bootstrap.LocationPicker} this
35657          * @param {Map event} e
35658          */
35659         mapRightClick : true,
35660         /**
35661          * @event markerClick
35662          * Fires when click the marker.
35663          * @param {Roo.bootstrap.LocationPicker} this
35664          * @param {Map event} e
35665          */
35666         markerClick : true,
35667         /**
35668          * @event markerRightClick
35669          * Fires when right click the marker.
35670          * @param {Roo.bootstrap.LocationPicker} this
35671          * @param {Map event} e
35672          */
35673         markerRightClick : true,
35674         /**
35675          * @event OverlayViewDraw
35676          * Fires when OverlayView Draw
35677          * @param {Roo.bootstrap.LocationPicker} this
35678          */
35679         OverlayViewDraw : true,
35680         /**
35681          * @event OverlayViewOnAdd
35682          * Fires when OverlayView Draw
35683          * @param {Roo.bootstrap.LocationPicker} this
35684          */
35685         OverlayViewOnAdd : true,
35686         /**
35687          * @event OverlayViewOnRemove
35688          * Fires when OverlayView Draw
35689          * @param {Roo.bootstrap.LocationPicker} this
35690          */
35691         OverlayViewOnRemove : true,
35692         /**
35693          * @event OverlayViewShow
35694          * Fires when OverlayView Draw
35695          * @param {Roo.bootstrap.LocationPicker} this
35696          * @param {Pixel} cpx
35697          */
35698         OverlayViewShow : true,
35699         /**
35700          * @event OverlayViewHide
35701          * Fires when OverlayView Draw
35702          * @param {Roo.bootstrap.LocationPicker} this
35703          */
35704         OverlayViewHide : true,
35705         /**
35706          * @event loadexception
35707          * Fires when load google lib failed.
35708          * @param {Roo.bootstrap.LocationPicker} this
35709          */
35710         loadexception : true
35711     });
35712         
35713 };
35714
35715 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
35716     
35717     gMapContext: false,
35718     
35719     latitude: 0,
35720     longitude: 0,
35721     zoom: 15,
35722     mapTypeId: false,
35723     mapTypeControl: false,
35724     disableDoubleClickZoom: false,
35725     scrollwheel: true,
35726     streetViewControl: false,
35727     radius: 0,
35728     locationName: '',
35729     draggable: true,
35730     enableAutocomplete: false,
35731     enableReverseGeocode: true,
35732     markerTitle: '',
35733     
35734     getAutoCreate: function()
35735     {
35736
35737         var cfg = {
35738             tag: 'div',
35739             cls: 'roo-location-picker'
35740         };
35741         
35742         return cfg
35743     },
35744     
35745     initEvents: function(ct, position)
35746     {       
35747         if(!this.el.getWidth() || this.isApplied()){
35748             return;
35749         }
35750         
35751         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35752         
35753         this.initial();
35754     },
35755     
35756     initial: function()
35757     {
35758         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35759             this.fireEvent('loadexception', this);
35760             return;
35761         }
35762         
35763         if(!this.mapTypeId){
35764             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35765         }
35766         
35767         this.gMapContext = this.GMapContext();
35768         
35769         this.initOverlayView();
35770         
35771         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35772         
35773         var _this = this;
35774                 
35775         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35776             _this.setPosition(_this.gMapContext.marker.position);
35777         });
35778         
35779         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35780             _this.fireEvent('mapClick', this, event);
35781             
35782         });
35783
35784         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35785             _this.fireEvent('mapRightClick', this, event);
35786             
35787         });
35788         
35789         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35790             _this.fireEvent('markerClick', this, event);
35791             
35792         });
35793
35794         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35795             _this.fireEvent('markerRightClick', this, event);
35796             
35797         });
35798         
35799         this.setPosition(this.gMapContext.location);
35800         
35801         this.fireEvent('initial', this, this.gMapContext.location);
35802     },
35803     
35804     initOverlayView: function()
35805     {
35806         var _this = this;
35807         
35808         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35809             
35810             draw: function()
35811             {
35812                 _this.fireEvent('OverlayViewDraw', _this);
35813             },
35814             
35815             onAdd: function()
35816             {
35817                 _this.fireEvent('OverlayViewOnAdd', _this);
35818             },
35819             
35820             onRemove: function()
35821             {
35822                 _this.fireEvent('OverlayViewOnRemove', _this);
35823             },
35824             
35825             show: function(cpx)
35826             {
35827                 _this.fireEvent('OverlayViewShow', _this, cpx);
35828             },
35829             
35830             hide: function()
35831             {
35832                 _this.fireEvent('OverlayViewHide', _this);
35833             }
35834             
35835         });
35836     },
35837     
35838     fromLatLngToContainerPixel: function(event)
35839     {
35840         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35841     },
35842     
35843     isApplied: function() 
35844     {
35845         return this.getGmapContext() == false ? false : true;
35846     },
35847     
35848     getGmapContext: function() 
35849     {
35850         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35851     },
35852     
35853     GMapContext: function() 
35854     {
35855         var position = new google.maps.LatLng(this.latitude, this.longitude);
35856         
35857         var _map = new google.maps.Map(this.el.dom, {
35858             center: position,
35859             zoom: this.zoom,
35860             mapTypeId: this.mapTypeId,
35861             mapTypeControl: this.mapTypeControl,
35862             disableDoubleClickZoom: this.disableDoubleClickZoom,
35863             scrollwheel: this.scrollwheel,
35864             streetViewControl: this.streetViewControl,
35865             locationName: this.locationName,
35866             draggable: this.draggable,
35867             enableAutocomplete: this.enableAutocomplete,
35868             enableReverseGeocode: this.enableReverseGeocode
35869         });
35870         
35871         var _marker = new google.maps.Marker({
35872             position: position,
35873             map: _map,
35874             title: this.markerTitle,
35875             draggable: this.draggable
35876         });
35877         
35878         return {
35879             map: _map,
35880             marker: _marker,
35881             circle: null,
35882             location: position,
35883             radius: this.radius,
35884             locationName: this.locationName,
35885             addressComponents: {
35886                 formatted_address: null,
35887                 addressLine1: null,
35888                 addressLine2: null,
35889                 streetName: null,
35890                 streetNumber: null,
35891                 city: null,
35892                 district: null,
35893                 state: null,
35894                 stateOrProvince: null
35895             },
35896             settings: this,
35897             domContainer: this.el.dom,
35898             geodecoder: new google.maps.Geocoder()
35899         };
35900     },
35901     
35902     drawCircle: function(center, radius, options) 
35903     {
35904         if (this.gMapContext.circle != null) {
35905             this.gMapContext.circle.setMap(null);
35906         }
35907         if (radius > 0) {
35908             radius *= 1;
35909             options = Roo.apply({}, options, {
35910                 strokeColor: "#0000FF",
35911                 strokeOpacity: .35,
35912                 strokeWeight: 2,
35913                 fillColor: "#0000FF",
35914                 fillOpacity: .2
35915             });
35916             
35917             options.map = this.gMapContext.map;
35918             options.radius = radius;
35919             options.center = center;
35920             this.gMapContext.circle = new google.maps.Circle(options);
35921             return this.gMapContext.circle;
35922         }
35923         
35924         return null;
35925     },
35926     
35927     setPosition: function(location) 
35928     {
35929         this.gMapContext.location = location;
35930         this.gMapContext.marker.setPosition(location);
35931         this.gMapContext.map.panTo(location);
35932         this.drawCircle(location, this.gMapContext.radius, {});
35933         
35934         var _this = this;
35935         
35936         if (this.gMapContext.settings.enableReverseGeocode) {
35937             this.gMapContext.geodecoder.geocode({
35938                 latLng: this.gMapContext.location
35939             }, function(results, status) {
35940                 
35941                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35942                     _this.gMapContext.locationName = results[0].formatted_address;
35943                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35944                     
35945                     _this.fireEvent('positionchanged', this, location);
35946                 }
35947             });
35948             
35949             return;
35950         }
35951         
35952         this.fireEvent('positionchanged', this, location);
35953     },
35954     
35955     resize: function()
35956     {
35957         google.maps.event.trigger(this.gMapContext.map, "resize");
35958         
35959         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35960         
35961         this.fireEvent('resize', this);
35962     },
35963     
35964     setPositionByLatLng: function(latitude, longitude)
35965     {
35966         this.setPosition(new google.maps.LatLng(latitude, longitude));
35967     },
35968     
35969     getCurrentPosition: function() 
35970     {
35971         return {
35972             latitude: this.gMapContext.location.lat(),
35973             longitude: this.gMapContext.location.lng()
35974         };
35975     },
35976     
35977     getAddressName: function() 
35978     {
35979         return this.gMapContext.locationName;
35980     },
35981     
35982     getAddressComponents: function() 
35983     {
35984         return this.gMapContext.addressComponents;
35985     },
35986     
35987     address_component_from_google_geocode: function(address_components) 
35988     {
35989         var result = {};
35990         
35991         for (var i = 0; i < address_components.length; i++) {
35992             var component = address_components[i];
35993             if (component.types.indexOf("postal_code") >= 0) {
35994                 result.postalCode = component.short_name;
35995             } else if (component.types.indexOf("street_number") >= 0) {
35996                 result.streetNumber = component.short_name;
35997             } else if (component.types.indexOf("route") >= 0) {
35998                 result.streetName = component.short_name;
35999             } else if (component.types.indexOf("neighborhood") >= 0) {
36000                 result.city = component.short_name;
36001             } else if (component.types.indexOf("locality") >= 0) {
36002                 result.city = component.short_name;
36003             } else if (component.types.indexOf("sublocality") >= 0) {
36004                 result.district = component.short_name;
36005             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
36006                 result.stateOrProvince = component.short_name;
36007             } else if (component.types.indexOf("country") >= 0) {
36008                 result.country = component.short_name;
36009             }
36010         }
36011         
36012         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
36013         result.addressLine2 = "";
36014         return result;
36015     },
36016     
36017     setZoomLevel: function(zoom)
36018     {
36019         this.gMapContext.map.setZoom(zoom);
36020     },
36021     
36022     show: function()
36023     {
36024         if(!this.el){
36025             return;
36026         }
36027         
36028         this.el.show();
36029         
36030         this.resize();
36031         
36032         this.fireEvent('show', this);
36033     },
36034     
36035     hide: function()
36036     {
36037         if(!this.el){
36038             return;
36039         }
36040         
36041         this.el.hide();
36042         
36043         this.fireEvent('hide', this);
36044     }
36045     
36046 });
36047
36048 Roo.apply(Roo.bootstrap.LocationPicker, {
36049     
36050     OverlayView : function(map, options)
36051     {
36052         options = options || {};
36053         
36054         this.setMap(map);
36055     }
36056     
36057     
36058 });/**
36059  * @class Roo.bootstrap.Alert
36060  * @extends Roo.bootstrap.Component
36061  * Bootstrap Alert class - shows an alert area box
36062  * eg
36063  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
36064   Enter a valid email address
36065 </div>
36066  * @licence LGPL
36067  * @cfg {String} title The title of alert
36068  * @cfg {String} html The content of alert
36069  * @cfg {String} weight (success|info|warning|danger) Weight of the message
36070  * @cfg {String} fa font-awesomeicon
36071  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
36072  * @cfg {Boolean} close true to show a x closer
36073  * 
36074  * 
36075  * @constructor
36076  * Create a new alert
36077  * @param {Object} config The config object
36078  */
36079
36080
36081 Roo.bootstrap.Alert = function(config){
36082     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
36083     
36084 };
36085
36086 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
36087     
36088     title: '',
36089     html: '',
36090     weight: false,
36091     fa: false,
36092     faicon: false, // BC
36093     close : false,
36094     
36095     
36096     getAutoCreate : function()
36097     {
36098         
36099         var cfg = {
36100             tag : 'div',
36101             cls : 'alert',
36102             cn : [
36103                 {
36104                     tag: 'button',
36105                     type :  "button",
36106                     cls: "close",
36107                     html : '×',
36108                     style : this.close ? '' : 'display:none'
36109                 },
36110                 {
36111                     tag : 'i',
36112                     cls : 'roo-alert-icon'
36113                     
36114                 },
36115                 {
36116                     tag : 'b',
36117                     cls : 'roo-alert-title',
36118                     html : this.title
36119                 },
36120                 {
36121                     tag : 'span',
36122                     cls : 'roo-alert-text',
36123                     html : this.html
36124                 }
36125             ]
36126         };
36127         
36128         if(this.faicon){
36129             cfg.cn[0].cls += ' fa ' + this.faicon;
36130         }
36131         if(this.fa){
36132             cfg.cn[0].cls += ' fa ' + this.fa;
36133         }
36134         
36135         if(this.weight){
36136             cfg.cls += ' alert-' + this.weight;
36137         }
36138         
36139         return cfg;
36140     },
36141     
36142     initEvents: function() 
36143     {
36144         this.el.setVisibilityMode(Roo.Element.DISPLAY);
36145         this.titleEl =  this.el.select('.roo-alert-title',true).first();
36146         this.iconEl = this.el.select('.roo-alert-icon',true).first();
36147         this.htmlEl = this.el.select('.roo-alert-text',true).first();
36148         if (this.seconds > 0) {
36149             this.hide.defer(this.seconds, this);
36150         }
36151     },
36152     /**
36153      * Set the Title Message HTML
36154      * @param {String} html
36155      */
36156     setTitle : function(str)
36157     {
36158         this.titleEl.dom.innerHTML = str;
36159     },
36160      
36161      /**
36162      * Set the Body Message HTML
36163      * @param {String} html
36164      */
36165     setHtml : function(str)
36166     {
36167         this.htmlEl.dom.innerHTML = str;
36168     },
36169     /**
36170      * Set the Weight of the alert
36171      * @param {String} (success|info|warning|danger) weight
36172      */
36173     
36174     setWeight : function(weight)
36175     {
36176         if(this.weight){
36177             this.el.removeClass('alert-' + this.weight);
36178         }
36179         
36180         this.weight = weight;
36181         
36182         this.el.addClass('alert-' + this.weight);
36183     },
36184       /**
36185      * Set the Icon of the alert
36186      * @param {String} see fontawsome names (name without the 'fa-' bit)
36187      */
36188     setIcon : function(icon)
36189     {
36190         if(this.faicon){
36191             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
36192         }
36193         
36194         this.faicon = icon;
36195         
36196         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
36197     },
36198     /**
36199      * Hide the Alert
36200      */
36201     hide: function() 
36202     {
36203         this.el.hide();   
36204     },
36205     /**
36206      * Show the Alert
36207      */
36208     show: function() 
36209     {  
36210         this.el.show();   
36211     }
36212     
36213 });
36214
36215  
36216 /*
36217 * Licence: LGPL
36218 */
36219
36220 /**
36221  * @class Roo.bootstrap.UploadCropbox
36222  * @extends Roo.bootstrap.Component
36223  * Bootstrap UploadCropbox class
36224  * @cfg {String} emptyText show when image has been loaded
36225  * @cfg {String} rotateNotify show when image too small to rotate
36226  * @cfg {Number} errorTimeout default 3000
36227  * @cfg {Number} minWidth default 300
36228  * @cfg {Number} minHeight default 300
36229  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
36230  * @cfg {Boolean} isDocument (true|false) default false
36231  * @cfg {String} url action url
36232  * @cfg {String} paramName default 'imageUpload'
36233  * @cfg {String} method default POST
36234  * @cfg {Boolean} loadMask (true|false) default true
36235  * @cfg {Boolean} loadingText default 'Loading...'
36236  * 
36237  * @constructor
36238  * Create a new UploadCropbox
36239  * @param {Object} config The config object
36240  */
36241
36242 Roo.bootstrap.UploadCropbox = function(config){
36243     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
36244     
36245     this.addEvents({
36246         /**
36247          * @event beforeselectfile
36248          * Fire before select file
36249          * @param {Roo.bootstrap.UploadCropbox} this
36250          */
36251         "beforeselectfile" : true,
36252         /**
36253          * @event initial
36254          * Fire after initEvent
36255          * @param {Roo.bootstrap.UploadCropbox} this
36256          */
36257         "initial" : true,
36258         /**
36259          * @event crop
36260          * Fire after initEvent
36261          * @param {Roo.bootstrap.UploadCropbox} this
36262          * @param {String} data
36263          */
36264         "crop" : true,
36265         /**
36266          * @event prepare
36267          * Fire when preparing the file data
36268          * @param {Roo.bootstrap.UploadCropbox} this
36269          * @param {Object} file
36270          */
36271         "prepare" : true,
36272         /**
36273          * @event exception
36274          * Fire when get exception
36275          * @param {Roo.bootstrap.UploadCropbox} this
36276          * @param {XMLHttpRequest} xhr
36277          */
36278         "exception" : true,
36279         /**
36280          * @event beforeloadcanvas
36281          * Fire before load the canvas
36282          * @param {Roo.bootstrap.UploadCropbox} this
36283          * @param {String} src
36284          */
36285         "beforeloadcanvas" : true,
36286         /**
36287          * @event trash
36288          * Fire when trash image
36289          * @param {Roo.bootstrap.UploadCropbox} this
36290          */
36291         "trash" : true,
36292         /**
36293          * @event download
36294          * Fire when download the image
36295          * @param {Roo.bootstrap.UploadCropbox} this
36296          */
36297         "download" : true,
36298         /**
36299          * @event footerbuttonclick
36300          * Fire when footerbuttonclick
36301          * @param {Roo.bootstrap.UploadCropbox} this
36302          * @param {String} type
36303          */
36304         "footerbuttonclick" : true,
36305         /**
36306          * @event resize
36307          * Fire when resize
36308          * @param {Roo.bootstrap.UploadCropbox} this
36309          */
36310         "resize" : true,
36311         /**
36312          * @event rotate
36313          * Fire when rotate the image
36314          * @param {Roo.bootstrap.UploadCropbox} this
36315          * @param {String} pos
36316          */
36317         "rotate" : true,
36318         /**
36319          * @event inspect
36320          * Fire when inspect the file
36321          * @param {Roo.bootstrap.UploadCropbox} this
36322          * @param {Object} file
36323          */
36324         "inspect" : true,
36325         /**
36326          * @event upload
36327          * Fire when xhr upload the file
36328          * @param {Roo.bootstrap.UploadCropbox} this
36329          * @param {Object} data
36330          */
36331         "upload" : true,
36332         /**
36333          * @event arrange
36334          * Fire when arrange the file data
36335          * @param {Roo.bootstrap.UploadCropbox} this
36336          * @param {Object} formData
36337          */
36338         "arrange" : true
36339     });
36340     
36341     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
36342 };
36343
36344 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
36345     
36346     emptyText : 'Click to upload image',
36347     rotateNotify : 'Image is too small to rotate',
36348     errorTimeout : 3000,
36349     scale : 0,
36350     baseScale : 1,
36351     rotate : 0,
36352     dragable : false,
36353     pinching : false,
36354     mouseX : 0,
36355     mouseY : 0,
36356     cropData : false,
36357     minWidth : 300,
36358     minHeight : 300,
36359     file : false,
36360     exif : {},
36361     baseRotate : 1,
36362     cropType : 'image/jpeg',
36363     buttons : false,
36364     canvasLoaded : false,
36365     isDocument : false,
36366     method : 'POST',
36367     paramName : 'imageUpload',
36368     loadMask : true,
36369     loadingText : 'Loading...',
36370     maskEl : false,
36371     
36372     getAutoCreate : function()
36373     {
36374         var cfg = {
36375             tag : 'div',
36376             cls : 'roo-upload-cropbox',
36377             cn : [
36378                 {
36379                     tag : 'input',
36380                     cls : 'roo-upload-cropbox-selector',
36381                     type : 'file'
36382                 },
36383                 {
36384                     tag : 'div',
36385                     cls : 'roo-upload-cropbox-body',
36386                     style : 'cursor:pointer',
36387                     cn : [
36388                         {
36389                             tag : 'div',
36390                             cls : 'roo-upload-cropbox-preview'
36391                         },
36392                         {
36393                             tag : 'div',
36394                             cls : 'roo-upload-cropbox-thumb'
36395                         },
36396                         {
36397                             tag : 'div',
36398                             cls : 'roo-upload-cropbox-empty-notify',
36399                             html : this.emptyText
36400                         },
36401                         {
36402                             tag : 'div',
36403                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
36404                             html : this.rotateNotify
36405                         }
36406                     ]
36407                 },
36408                 {
36409                     tag : 'div',
36410                     cls : 'roo-upload-cropbox-footer',
36411                     cn : {
36412                         tag : 'div',
36413                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
36414                         cn : []
36415                     }
36416                 }
36417             ]
36418         };
36419         
36420         return cfg;
36421     },
36422     
36423     onRender : function(ct, position)
36424     {
36425         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
36426         
36427         if (this.buttons.length) {
36428             
36429             Roo.each(this.buttons, function(bb) {
36430                 
36431                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
36432                 
36433                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
36434                 
36435             }, this);
36436         }
36437         
36438         if(this.loadMask){
36439             this.maskEl = this.el;
36440         }
36441     },
36442     
36443     initEvents : function()
36444     {
36445         this.urlAPI = (window.createObjectURL && window) || 
36446                                 (window.URL && URL.revokeObjectURL && URL) || 
36447                                 (window.webkitURL && webkitURL);
36448                         
36449         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
36450         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36451         
36452         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
36453         this.selectorEl.hide();
36454         
36455         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
36456         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36457         
36458         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
36459         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36460         this.thumbEl.hide();
36461         
36462         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
36463         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36464         
36465         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
36466         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36467         this.errorEl.hide();
36468         
36469         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
36470         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36471         this.footerEl.hide();
36472         
36473         this.setThumbBoxSize();
36474         
36475         this.bind();
36476         
36477         this.resize();
36478         
36479         this.fireEvent('initial', this);
36480     },
36481
36482     bind : function()
36483     {
36484         var _this = this;
36485         
36486         window.addEventListener("resize", function() { _this.resize(); } );
36487         
36488         this.bodyEl.on('click', this.beforeSelectFile, this);
36489         
36490         if(Roo.isTouch){
36491             this.bodyEl.on('touchstart', this.onTouchStart, this);
36492             this.bodyEl.on('touchmove', this.onTouchMove, this);
36493             this.bodyEl.on('touchend', this.onTouchEnd, this);
36494         }
36495         
36496         if(!Roo.isTouch){
36497             this.bodyEl.on('mousedown', this.onMouseDown, this);
36498             this.bodyEl.on('mousemove', this.onMouseMove, this);
36499             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
36500             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
36501             Roo.get(document).on('mouseup', this.onMouseUp, this);
36502         }
36503         
36504         this.selectorEl.on('change', this.onFileSelected, this);
36505     },
36506     
36507     reset : function()
36508     {    
36509         this.scale = 0;
36510         this.baseScale = 1;
36511         this.rotate = 0;
36512         this.baseRotate = 1;
36513         this.dragable = false;
36514         this.pinching = false;
36515         this.mouseX = 0;
36516         this.mouseY = 0;
36517         this.cropData = false;
36518         this.notifyEl.dom.innerHTML = this.emptyText;
36519         
36520         this.selectorEl.dom.value = '';
36521         
36522     },
36523     
36524     resize : function()
36525     {
36526         if(this.fireEvent('resize', this) != false){
36527             this.setThumbBoxPosition();
36528             this.setCanvasPosition();
36529         }
36530     },
36531     
36532     onFooterButtonClick : function(e, el, o, type)
36533     {
36534         switch (type) {
36535             case 'rotate-left' :
36536                 this.onRotateLeft(e);
36537                 break;
36538             case 'rotate-right' :
36539                 this.onRotateRight(e);
36540                 break;
36541             case 'picture' :
36542                 this.beforeSelectFile(e);
36543                 break;
36544             case 'trash' :
36545                 this.trash(e);
36546                 break;
36547             case 'crop' :
36548                 this.crop(e);
36549                 break;
36550             case 'download' :
36551                 this.download(e);
36552                 break;
36553             default :
36554                 break;
36555         }
36556         
36557         this.fireEvent('footerbuttonclick', this, type);
36558     },
36559     
36560     beforeSelectFile : function(e)
36561     {
36562         e.preventDefault();
36563         
36564         if(this.fireEvent('beforeselectfile', this) != false){
36565             this.selectorEl.dom.click();
36566         }
36567     },
36568     
36569     onFileSelected : function(e)
36570     {
36571         e.preventDefault();
36572         
36573         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36574             return;
36575         }
36576         
36577         var file = this.selectorEl.dom.files[0];
36578         
36579         if(this.fireEvent('inspect', this, file) != false){
36580             this.prepare(file);
36581         }
36582         
36583     },
36584     
36585     trash : function(e)
36586     {
36587         this.fireEvent('trash', this);
36588     },
36589     
36590     download : function(e)
36591     {
36592         this.fireEvent('download', this);
36593     },
36594     
36595     loadCanvas : function(src)
36596     {   
36597         if(this.fireEvent('beforeloadcanvas', this, src) != false){
36598             
36599             this.reset();
36600             
36601             this.imageEl = document.createElement('img');
36602             
36603             var _this = this;
36604             
36605             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36606             
36607             this.imageEl.src = src;
36608         }
36609     },
36610     
36611     onLoadCanvas : function()
36612     {   
36613         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36614         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36615         
36616         this.bodyEl.un('click', this.beforeSelectFile, this);
36617         
36618         this.notifyEl.hide();
36619         this.thumbEl.show();
36620         this.footerEl.show();
36621         
36622         this.baseRotateLevel();
36623         
36624         if(this.isDocument){
36625             this.setThumbBoxSize();
36626         }
36627         
36628         this.setThumbBoxPosition();
36629         
36630         this.baseScaleLevel();
36631         
36632         this.draw();
36633         
36634         this.resize();
36635         
36636         this.canvasLoaded = true;
36637         
36638         if(this.loadMask){
36639             this.maskEl.unmask();
36640         }
36641         
36642     },
36643     
36644     setCanvasPosition : function()
36645     {   
36646         if(!this.canvasEl){
36647             return;
36648         }
36649         
36650         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36651         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36652         
36653         this.previewEl.setLeft(pw);
36654         this.previewEl.setTop(ph);
36655         
36656     },
36657     
36658     onMouseDown : function(e)
36659     {   
36660         e.stopEvent();
36661         
36662         this.dragable = true;
36663         this.pinching = false;
36664         
36665         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36666             this.dragable = false;
36667             return;
36668         }
36669         
36670         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36671         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36672         
36673     },
36674     
36675     onMouseMove : function(e)
36676     {   
36677         e.stopEvent();
36678         
36679         if(!this.canvasLoaded){
36680             return;
36681         }
36682         
36683         if (!this.dragable){
36684             return;
36685         }
36686         
36687         var minX = Math.ceil(this.thumbEl.getLeft(true));
36688         var minY = Math.ceil(this.thumbEl.getTop(true));
36689         
36690         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36691         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36692         
36693         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36694         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36695         
36696         x = x - this.mouseX;
36697         y = y - this.mouseY;
36698         
36699         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36700         var bgY = Math.ceil(y + this.previewEl.getTop(true));
36701         
36702         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36703         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36704         
36705         this.previewEl.setLeft(bgX);
36706         this.previewEl.setTop(bgY);
36707         
36708         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36709         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36710     },
36711     
36712     onMouseUp : function(e)
36713     {   
36714         e.stopEvent();
36715         
36716         this.dragable = false;
36717     },
36718     
36719     onMouseWheel : function(e)
36720     {   
36721         e.stopEvent();
36722         
36723         this.startScale = this.scale;
36724         
36725         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36726         
36727         if(!this.zoomable()){
36728             this.scale = this.startScale;
36729             return;
36730         }
36731         
36732         this.draw();
36733         
36734         return;
36735     },
36736     
36737     zoomable : function()
36738     {
36739         var minScale = this.thumbEl.getWidth() / this.minWidth;
36740         
36741         if(this.minWidth < this.minHeight){
36742             minScale = this.thumbEl.getHeight() / this.minHeight;
36743         }
36744         
36745         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36746         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36747         
36748         if(
36749                 this.isDocument &&
36750                 (this.rotate == 0 || this.rotate == 180) && 
36751                 (
36752                     width > this.imageEl.OriginWidth || 
36753                     height > this.imageEl.OriginHeight ||
36754                     (width < this.minWidth && height < this.minHeight)
36755                 )
36756         ){
36757             return false;
36758         }
36759         
36760         if(
36761                 this.isDocument &&
36762                 (this.rotate == 90 || this.rotate == 270) && 
36763                 (
36764                     width > this.imageEl.OriginWidth || 
36765                     height > this.imageEl.OriginHeight ||
36766                     (width < this.minHeight && height < this.minWidth)
36767                 )
36768         ){
36769             return false;
36770         }
36771         
36772         if(
36773                 !this.isDocument &&
36774                 (this.rotate == 0 || this.rotate == 180) && 
36775                 (
36776                     width < this.minWidth || 
36777                     width > this.imageEl.OriginWidth || 
36778                     height < this.minHeight || 
36779                     height > this.imageEl.OriginHeight
36780                 )
36781         ){
36782             return false;
36783         }
36784         
36785         if(
36786                 !this.isDocument &&
36787                 (this.rotate == 90 || this.rotate == 270) && 
36788                 (
36789                     width < this.minHeight || 
36790                     width > this.imageEl.OriginWidth || 
36791                     height < this.minWidth || 
36792                     height > this.imageEl.OriginHeight
36793                 )
36794         ){
36795             return false;
36796         }
36797         
36798         return true;
36799         
36800     },
36801     
36802     onRotateLeft : function(e)
36803     {   
36804         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36805             
36806             var minScale = this.thumbEl.getWidth() / this.minWidth;
36807             
36808             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36809             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36810             
36811             this.startScale = this.scale;
36812             
36813             while (this.getScaleLevel() < minScale){
36814             
36815                 this.scale = this.scale + 1;
36816                 
36817                 if(!this.zoomable()){
36818                     break;
36819                 }
36820                 
36821                 if(
36822                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36823                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36824                 ){
36825                     continue;
36826                 }
36827                 
36828                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36829
36830                 this.draw();
36831                 
36832                 return;
36833             }
36834             
36835             this.scale = this.startScale;
36836             
36837             this.onRotateFail();
36838             
36839             return false;
36840         }
36841         
36842         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36843
36844         if(this.isDocument){
36845             this.setThumbBoxSize();
36846             this.setThumbBoxPosition();
36847             this.setCanvasPosition();
36848         }
36849         
36850         this.draw();
36851         
36852         this.fireEvent('rotate', this, 'left');
36853         
36854     },
36855     
36856     onRotateRight : function(e)
36857     {
36858         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36859             
36860             var minScale = this.thumbEl.getWidth() / this.minWidth;
36861         
36862             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36863             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36864             
36865             this.startScale = this.scale;
36866             
36867             while (this.getScaleLevel() < minScale){
36868             
36869                 this.scale = this.scale + 1;
36870                 
36871                 if(!this.zoomable()){
36872                     break;
36873                 }
36874                 
36875                 if(
36876                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36877                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36878                 ){
36879                     continue;
36880                 }
36881                 
36882                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36883
36884                 this.draw();
36885                 
36886                 return;
36887             }
36888             
36889             this.scale = this.startScale;
36890             
36891             this.onRotateFail();
36892             
36893             return false;
36894         }
36895         
36896         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36897
36898         if(this.isDocument){
36899             this.setThumbBoxSize();
36900             this.setThumbBoxPosition();
36901             this.setCanvasPosition();
36902         }
36903         
36904         this.draw();
36905         
36906         this.fireEvent('rotate', this, 'right');
36907     },
36908     
36909     onRotateFail : function()
36910     {
36911         this.errorEl.show(true);
36912         
36913         var _this = this;
36914         
36915         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36916     },
36917     
36918     draw : function()
36919     {
36920         this.previewEl.dom.innerHTML = '';
36921         
36922         var canvasEl = document.createElement("canvas");
36923         
36924         var contextEl = canvasEl.getContext("2d");
36925         
36926         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36927         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36928         var center = this.imageEl.OriginWidth / 2;
36929         
36930         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36931             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36932             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36933             center = this.imageEl.OriginHeight / 2;
36934         }
36935         
36936         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36937         
36938         contextEl.translate(center, center);
36939         contextEl.rotate(this.rotate * Math.PI / 180);
36940
36941         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36942         
36943         this.canvasEl = document.createElement("canvas");
36944         
36945         this.contextEl = this.canvasEl.getContext("2d");
36946         
36947         switch (this.rotate) {
36948             case 0 :
36949                 
36950                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36951                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36952                 
36953                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36954                 
36955                 break;
36956             case 90 : 
36957                 
36958                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36959                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36960                 
36961                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36962                     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);
36963                     break;
36964                 }
36965                 
36966                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36967                 
36968                 break;
36969             case 180 :
36970                 
36971                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36972                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36973                 
36974                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36975                     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);
36976                     break;
36977                 }
36978                 
36979                 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);
36980                 
36981                 break;
36982             case 270 :
36983                 
36984                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36985                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36986         
36987                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36988                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36989                     break;
36990                 }
36991                 
36992                 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);
36993                 
36994                 break;
36995             default : 
36996                 break;
36997         }
36998         
36999         this.previewEl.appendChild(this.canvasEl);
37000         
37001         this.setCanvasPosition();
37002     },
37003     
37004     crop : function()
37005     {
37006         if(!this.canvasLoaded){
37007             return;
37008         }
37009         
37010         var imageCanvas = document.createElement("canvas");
37011         
37012         var imageContext = imageCanvas.getContext("2d");
37013         
37014         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
37015         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
37016         
37017         var center = imageCanvas.width / 2;
37018         
37019         imageContext.translate(center, center);
37020         
37021         imageContext.rotate(this.rotate * Math.PI / 180);
37022         
37023         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
37024         
37025         var canvas = document.createElement("canvas");
37026         
37027         var context = canvas.getContext("2d");
37028                 
37029         canvas.width = this.minWidth;
37030         canvas.height = this.minHeight;
37031
37032         switch (this.rotate) {
37033             case 0 :
37034                 
37035                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
37036                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
37037                 
37038                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
37039                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
37040                 
37041                 var targetWidth = this.minWidth - 2 * x;
37042                 var targetHeight = this.minHeight - 2 * y;
37043                 
37044                 var scale = 1;
37045                 
37046                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
37047                     scale = targetWidth / width;
37048                 }
37049                 
37050                 if(x > 0 && y == 0){
37051                     scale = targetHeight / height;
37052                 }
37053                 
37054                 if(x > 0 && y > 0){
37055                     scale = targetWidth / width;
37056                     
37057                     if(width < height){
37058                         scale = targetHeight / height;
37059                     }
37060                 }
37061                 
37062                 context.scale(scale, scale);
37063                 
37064                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
37065                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
37066
37067                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
37068                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
37069
37070                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
37071                 
37072                 break;
37073             case 90 : 
37074                 
37075                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
37076                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
37077                 
37078                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
37079                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
37080                 
37081                 var targetWidth = this.minWidth - 2 * x;
37082                 var targetHeight = this.minHeight - 2 * y;
37083                 
37084                 var scale = 1;
37085                 
37086                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
37087                     scale = targetWidth / width;
37088                 }
37089                 
37090                 if(x > 0 && y == 0){
37091                     scale = targetHeight / height;
37092                 }
37093                 
37094                 if(x > 0 && y > 0){
37095                     scale = targetWidth / width;
37096                     
37097                     if(width < height){
37098                         scale = targetHeight / height;
37099                     }
37100                 }
37101                 
37102                 context.scale(scale, scale);
37103                 
37104                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
37105                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
37106
37107                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
37108                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
37109                 
37110                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
37111                 
37112                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
37113                 
37114                 break;
37115             case 180 :
37116                 
37117                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
37118                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
37119                 
37120                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
37121                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
37122                 
37123                 var targetWidth = this.minWidth - 2 * x;
37124                 var targetHeight = this.minHeight - 2 * y;
37125                 
37126                 var scale = 1;
37127                 
37128                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
37129                     scale = targetWidth / width;
37130                 }
37131                 
37132                 if(x > 0 && y == 0){
37133                     scale = targetHeight / height;
37134                 }
37135                 
37136                 if(x > 0 && y > 0){
37137                     scale = targetWidth / width;
37138                     
37139                     if(width < height){
37140                         scale = targetHeight / height;
37141                     }
37142                 }
37143                 
37144                 context.scale(scale, scale);
37145                 
37146                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
37147                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
37148
37149                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
37150                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
37151
37152                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
37153                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
37154                 
37155                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
37156                 
37157                 break;
37158             case 270 :
37159                 
37160                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
37161                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
37162                 
37163                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
37164                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
37165                 
37166                 var targetWidth = this.minWidth - 2 * x;
37167                 var targetHeight = this.minHeight - 2 * y;
37168                 
37169                 var scale = 1;
37170                 
37171                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
37172                     scale = targetWidth / width;
37173                 }
37174                 
37175                 if(x > 0 && y == 0){
37176                     scale = targetHeight / height;
37177                 }
37178                 
37179                 if(x > 0 && y > 0){
37180                     scale = targetWidth / width;
37181                     
37182                     if(width < height){
37183                         scale = targetHeight / height;
37184                     }
37185                 }
37186                 
37187                 context.scale(scale, scale);
37188                 
37189                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
37190                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
37191
37192                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
37193                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
37194                 
37195                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
37196                 
37197                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
37198                 
37199                 break;
37200             default : 
37201                 break;
37202         }
37203         
37204         this.cropData = canvas.toDataURL(this.cropType);
37205         
37206         if(this.fireEvent('crop', this, this.cropData) !== false){
37207             this.process(this.file, this.cropData);
37208         }
37209         
37210         return;
37211         
37212     },
37213     
37214     setThumbBoxSize : function()
37215     {
37216         var width, height;
37217         
37218         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
37219             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
37220             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
37221             
37222             this.minWidth = width;
37223             this.minHeight = height;
37224             
37225             if(this.rotate == 90 || this.rotate == 270){
37226                 this.minWidth = height;
37227                 this.minHeight = width;
37228             }
37229         }
37230         
37231         height = 300;
37232         width = Math.ceil(this.minWidth * height / this.minHeight);
37233         
37234         if(this.minWidth > this.minHeight){
37235             width = 300;
37236             height = Math.ceil(this.minHeight * width / this.minWidth);
37237         }
37238         
37239         this.thumbEl.setStyle({
37240             width : width + 'px',
37241             height : height + 'px'
37242         });
37243
37244         return;
37245             
37246     },
37247     
37248     setThumbBoxPosition : function()
37249     {
37250         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
37251         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
37252         
37253         this.thumbEl.setLeft(x);
37254         this.thumbEl.setTop(y);
37255         
37256     },
37257     
37258     baseRotateLevel : function()
37259     {
37260         this.baseRotate = 1;
37261         
37262         if(
37263                 typeof(this.exif) != 'undefined' &&
37264                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
37265                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
37266         ){
37267             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
37268         }
37269         
37270         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
37271         
37272     },
37273     
37274     baseScaleLevel : function()
37275     {
37276         var width, height;
37277         
37278         if(this.isDocument){
37279             
37280             if(this.baseRotate == 6 || this.baseRotate == 8){
37281             
37282                 height = this.thumbEl.getHeight();
37283                 this.baseScale = height / this.imageEl.OriginWidth;
37284
37285                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
37286                     width = this.thumbEl.getWidth();
37287                     this.baseScale = width / this.imageEl.OriginHeight;
37288                 }
37289
37290                 return;
37291             }
37292
37293             height = this.thumbEl.getHeight();
37294             this.baseScale = height / this.imageEl.OriginHeight;
37295
37296             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
37297                 width = this.thumbEl.getWidth();
37298                 this.baseScale = width / this.imageEl.OriginWidth;
37299             }
37300
37301             return;
37302         }
37303         
37304         if(this.baseRotate == 6 || this.baseRotate == 8){
37305             
37306             width = this.thumbEl.getHeight();
37307             this.baseScale = width / this.imageEl.OriginHeight;
37308             
37309             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
37310                 height = this.thumbEl.getWidth();
37311                 this.baseScale = height / this.imageEl.OriginHeight;
37312             }
37313             
37314             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
37315                 height = this.thumbEl.getWidth();
37316                 this.baseScale = height / this.imageEl.OriginHeight;
37317                 
37318                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
37319                     width = this.thumbEl.getHeight();
37320                     this.baseScale = width / this.imageEl.OriginWidth;
37321                 }
37322             }
37323             
37324             return;
37325         }
37326         
37327         width = this.thumbEl.getWidth();
37328         this.baseScale = width / this.imageEl.OriginWidth;
37329         
37330         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
37331             height = this.thumbEl.getHeight();
37332             this.baseScale = height / this.imageEl.OriginHeight;
37333         }
37334         
37335         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
37336             
37337             height = this.thumbEl.getHeight();
37338             this.baseScale = height / this.imageEl.OriginHeight;
37339             
37340             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
37341                 width = this.thumbEl.getWidth();
37342                 this.baseScale = width / this.imageEl.OriginWidth;
37343             }
37344             
37345         }
37346         
37347         return;
37348     },
37349     
37350     getScaleLevel : function()
37351     {
37352         return this.baseScale * Math.pow(1.1, this.scale);
37353     },
37354     
37355     onTouchStart : function(e)
37356     {
37357         if(!this.canvasLoaded){
37358             this.beforeSelectFile(e);
37359             return;
37360         }
37361         
37362         var touches = e.browserEvent.touches;
37363         
37364         if(!touches){
37365             return;
37366         }
37367         
37368         if(touches.length == 1){
37369             this.onMouseDown(e);
37370             return;
37371         }
37372         
37373         if(touches.length != 2){
37374             return;
37375         }
37376         
37377         var coords = [];
37378         
37379         for(var i = 0, finger; finger = touches[i]; i++){
37380             coords.push(finger.pageX, finger.pageY);
37381         }
37382         
37383         var x = Math.pow(coords[0] - coords[2], 2);
37384         var y = Math.pow(coords[1] - coords[3], 2);
37385         
37386         this.startDistance = Math.sqrt(x + y);
37387         
37388         this.startScale = this.scale;
37389         
37390         this.pinching = true;
37391         this.dragable = false;
37392         
37393     },
37394     
37395     onTouchMove : function(e)
37396     {
37397         if(!this.pinching && !this.dragable){
37398             return;
37399         }
37400         
37401         var touches = e.browserEvent.touches;
37402         
37403         if(!touches){
37404             return;
37405         }
37406         
37407         if(this.dragable){
37408             this.onMouseMove(e);
37409             return;
37410         }
37411         
37412         var coords = [];
37413         
37414         for(var i = 0, finger; finger = touches[i]; i++){
37415             coords.push(finger.pageX, finger.pageY);
37416         }
37417         
37418         var x = Math.pow(coords[0] - coords[2], 2);
37419         var y = Math.pow(coords[1] - coords[3], 2);
37420         
37421         this.endDistance = Math.sqrt(x + y);
37422         
37423         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
37424         
37425         if(!this.zoomable()){
37426             this.scale = this.startScale;
37427             return;
37428         }
37429         
37430         this.draw();
37431         
37432     },
37433     
37434     onTouchEnd : function(e)
37435     {
37436         this.pinching = false;
37437         this.dragable = false;
37438         
37439     },
37440     
37441     process : function(file, crop)
37442     {
37443         if(this.loadMask){
37444             this.maskEl.mask(this.loadingText);
37445         }
37446         
37447         this.xhr = new XMLHttpRequest();
37448         
37449         file.xhr = this.xhr;
37450
37451         this.xhr.open(this.method, this.url, true);
37452         
37453         var headers = {
37454             "Accept": "application/json",
37455             "Cache-Control": "no-cache",
37456             "X-Requested-With": "XMLHttpRequest"
37457         };
37458         
37459         for (var headerName in headers) {
37460             var headerValue = headers[headerName];
37461             if (headerValue) {
37462                 this.xhr.setRequestHeader(headerName, headerValue);
37463             }
37464         }
37465         
37466         var _this = this;
37467         
37468         this.xhr.onload = function()
37469         {
37470             _this.xhrOnLoad(_this.xhr);
37471         }
37472         
37473         this.xhr.onerror = function()
37474         {
37475             _this.xhrOnError(_this.xhr);
37476         }
37477         
37478         var formData = new FormData();
37479
37480         formData.append('returnHTML', 'NO');
37481         
37482         if(crop){
37483             formData.append('crop', crop);
37484         }
37485         
37486         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
37487             formData.append(this.paramName, file, file.name);
37488         }
37489         
37490         if(typeof(file.filename) != 'undefined'){
37491             formData.append('filename', file.filename);
37492         }
37493         
37494         if(typeof(file.mimetype) != 'undefined'){
37495             formData.append('mimetype', file.mimetype);
37496         }
37497         
37498         if(this.fireEvent('arrange', this, formData) != false){
37499             this.xhr.send(formData);
37500         };
37501     },
37502     
37503     xhrOnLoad : function(xhr)
37504     {
37505         if(this.loadMask){
37506             this.maskEl.unmask();
37507         }
37508         
37509         if (xhr.readyState !== 4) {
37510             this.fireEvent('exception', this, xhr);
37511             return;
37512         }
37513
37514         var response = Roo.decode(xhr.responseText);
37515         
37516         if(!response.success){
37517             this.fireEvent('exception', this, xhr);
37518             return;
37519         }
37520         
37521         var response = Roo.decode(xhr.responseText);
37522         
37523         this.fireEvent('upload', this, response);
37524         
37525     },
37526     
37527     xhrOnError : function()
37528     {
37529         if(this.loadMask){
37530             this.maskEl.unmask();
37531         }
37532         
37533         Roo.log('xhr on error');
37534         
37535         var response = Roo.decode(xhr.responseText);
37536           
37537         Roo.log(response);
37538         
37539     },
37540     
37541     prepare : function(file)
37542     {   
37543         if(this.loadMask){
37544             this.maskEl.mask(this.loadingText);
37545         }
37546         
37547         this.file = false;
37548         this.exif = {};
37549         
37550         if(typeof(file) === 'string'){
37551             this.loadCanvas(file);
37552             return;
37553         }
37554         
37555         if(!file || !this.urlAPI){
37556             return;
37557         }
37558         
37559         this.file = file;
37560         this.cropType = file.type;
37561         
37562         var _this = this;
37563         
37564         if(this.fireEvent('prepare', this, this.file) != false){
37565             
37566             var reader = new FileReader();
37567             
37568             reader.onload = function (e) {
37569                 if (e.target.error) {
37570                     Roo.log(e.target.error);
37571                     return;
37572                 }
37573                 
37574                 var buffer = e.target.result,
37575                     dataView = new DataView(buffer),
37576                     offset = 2,
37577                     maxOffset = dataView.byteLength - 4,
37578                     markerBytes,
37579                     markerLength;
37580                 
37581                 if (dataView.getUint16(0) === 0xffd8) {
37582                     while (offset < maxOffset) {
37583                         markerBytes = dataView.getUint16(offset);
37584                         
37585                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37586                             markerLength = dataView.getUint16(offset + 2) + 2;
37587                             if (offset + markerLength > dataView.byteLength) {
37588                                 Roo.log('Invalid meta data: Invalid segment size.');
37589                                 break;
37590                             }
37591                             
37592                             if(markerBytes == 0xffe1){
37593                                 _this.parseExifData(
37594                                     dataView,
37595                                     offset,
37596                                     markerLength
37597                                 );
37598                             }
37599                             
37600                             offset += markerLength;
37601                             
37602                             continue;
37603                         }
37604                         
37605                         break;
37606                     }
37607                     
37608                 }
37609                 
37610                 var url = _this.urlAPI.createObjectURL(_this.file);
37611                 
37612                 _this.loadCanvas(url);
37613                 
37614                 return;
37615             }
37616             
37617             reader.readAsArrayBuffer(this.file);
37618             
37619         }
37620         
37621     },
37622     
37623     parseExifData : function(dataView, offset, length)
37624     {
37625         var tiffOffset = offset + 10,
37626             littleEndian,
37627             dirOffset;
37628     
37629         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37630             // No Exif data, might be XMP data instead
37631             return;
37632         }
37633         
37634         // Check for the ASCII code for "Exif" (0x45786966):
37635         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37636             // No Exif data, might be XMP data instead
37637             return;
37638         }
37639         if (tiffOffset + 8 > dataView.byteLength) {
37640             Roo.log('Invalid Exif data: Invalid segment size.');
37641             return;
37642         }
37643         // Check for the two null bytes:
37644         if (dataView.getUint16(offset + 8) !== 0x0000) {
37645             Roo.log('Invalid Exif data: Missing byte alignment offset.');
37646             return;
37647         }
37648         // Check the byte alignment:
37649         switch (dataView.getUint16(tiffOffset)) {
37650         case 0x4949:
37651             littleEndian = true;
37652             break;
37653         case 0x4D4D:
37654             littleEndian = false;
37655             break;
37656         default:
37657             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37658             return;
37659         }
37660         // Check for the TIFF tag marker (0x002A):
37661         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37662             Roo.log('Invalid Exif data: Missing TIFF marker.');
37663             return;
37664         }
37665         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37666         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37667         
37668         this.parseExifTags(
37669             dataView,
37670             tiffOffset,
37671             tiffOffset + dirOffset,
37672             littleEndian
37673         );
37674     },
37675     
37676     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37677     {
37678         var tagsNumber,
37679             dirEndOffset,
37680             i;
37681         if (dirOffset + 6 > dataView.byteLength) {
37682             Roo.log('Invalid Exif data: Invalid directory offset.');
37683             return;
37684         }
37685         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37686         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37687         if (dirEndOffset + 4 > dataView.byteLength) {
37688             Roo.log('Invalid Exif data: Invalid directory size.');
37689             return;
37690         }
37691         for (i = 0; i < tagsNumber; i += 1) {
37692             this.parseExifTag(
37693                 dataView,
37694                 tiffOffset,
37695                 dirOffset + 2 + 12 * i, // tag offset
37696                 littleEndian
37697             );
37698         }
37699         // Return the offset to the next directory:
37700         return dataView.getUint32(dirEndOffset, littleEndian);
37701     },
37702     
37703     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
37704     {
37705         var tag = dataView.getUint16(offset, littleEndian);
37706         
37707         this.exif[tag] = this.getExifValue(
37708             dataView,
37709             tiffOffset,
37710             offset,
37711             dataView.getUint16(offset + 2, littleEndian), // tag type
37712             dataView.getUint32(offset + 4, littleEndian), // tag length
37713             littleEndian
37714         );
37715     },
37716     
37717     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37718     {
37719         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37720             tagSize,
37721             dataOffset,
37722             values,
37723             i,
37724             str,
37725             c;
37726     
37727         if (!tagType) {
37728             Roo.log('Invalid Exif data: Invalid tag type.');
37729             return;
37730         }
37731         
37732         tagSize = tagType.size * length;
37733         // Determine if the value is contained in the dataOffset bytes,
37734         // or if the value at the dataOffset is a pointer to the actual data:
37735         dataOffset = tagSize > 4 ?
37736                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37737         if (dataOffset + tagSize > dataView.byteLength) {
37738             Roo.log('Invalid Exif data: Invalid data offset.');
37739             return;
37740         }
37741         if (length === 1) {
37742             return tagType.getValue(dataView, dataOffset, littleEndian);
37743         }
37744         values = [];
37745         for (i = 0; i < length; i += 1) {
37746             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37747         }
37748         
37749         if (tagType.ascii) {
37750             str = '';
37751             // Concatenate the chars:
37752             for (i = 0; i < values.length; i += 1) {
37753                 c = values[i];
37754                 // Ignore the terminating NULL byte(s):
37755                 if (c === '\u0000') {
37756                     break;
37757                 }
37758                 str += c;
37759             }
37760             return str;
37761         }
37762         return values;
37763     }
37764     
37765 });
37766
37767 Roo.apply(Roo.bootstrap.UploadCropbox, {
37768     tags : {
37769         'Orientation': 0x0112
37770     },
37771     
37772     Orientation: {
37773             1: 0, //'top-left',
37774 //            2: 'top-right',
37775             3: 180, //'bottom-right',
37776 //            4: 'bottom-left',
37777 //            5: 'left-top',
37778             6: 90, //'right-top',
37779 //            7: 'right-bottom',
37780             8: 270 //'left-bottom'
37781     },
37782     
37783     exifTagTypes : {
37784         // byte, 8-bit unsigned int:
37785         1: {
37786             getValue: function (dataView, dataOffset) {
37787                 return dataView.getUint8(dataOffset);
37788             },
37789             size: 1
37790         },
37791         // ascii, 8-bit byte:
37792         2: {
37793             getValue: function (dataView, dataOffset) {
37794                 return String.fromCharCode(dataView.getUint8(dataOffset));
37795             },
37796             size: 1,
37797             ascii: true
37798         },
37799         // short, 16 bit int:
37800         3: {
37801             getValue: function (dataView, dataOffset, littleEndian) {
37802                 return dataView.getUint16(dataOffset, littleEndian);
37803             },
37804             size: 2
37805         },
37806         // long, 32 bit int:
37807         4: {
37808             getValue: function (dataView, dataOffset, littleEndian) {
37809                 return dataView.getUint32(dataOffset, littleEndian);
37810             },
37811             size: 4
37812         },
37813         // rational = two long values, first is numerator, second is denominator:
37814         5: {
37815             getValue: function (dataView, dataOffset, littleEndian) {
37816                 return dataView.getUint32(dataOffset, littleEndian) /
37817                     dataView.getUint32(dataOffset + 4, littleEndian);
37818             },
37819             size: 8
37820         },
37821         // slong, 32 bit signed int:
37822         9: {
37823             getValue: function (dataView, dataOffset, littleEndian) {
37824                 return dataView.getInt32(dataOffset, littleEndian);
37825             },
37826             size: 4
37827         },
37828         // srational, two slongs, first is numerator, second is denominator:
37829         10: {
37830             getValue: function (dataView, dataOffset, littleEndian) {
37831                 return dataView.getInt32(dataOffset, littleEndian) /
37832                     dataView.getInt32(dataOffset + 4, littleEndian);
37833             },
37834             size: 8
37835         }
37836     },
37837     
37838     footer : {
37839         STANDARD : [
37840             {
37841                 tag : 'div',
37842                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37843                 action : 'rotate-left',
37844                 cn : [
37845                     {
37846                         tag : 'button',
37847                         cls : 'btn btn-default',
37848                         html : '<i class="fa fa-undo"></i>'
37849                     }
37850                 ]
37851             },
37852             {
37853                 tag : 'div',
37854                 cls : 'btn-group roo-upload-cropbox-picture',
37855                 action : 'picture',
37856                 cn : [
37857                     {
37858                         tag : 'button',
37859                         cls : 'btn btn-default',
37860                         html : '<i class="fa fa-picture-o"></i>'
37861                     }
37862                 ]
37863             },
37864             {
37865                 tag : 'div',
37866                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37867                 action : 'rotate-right',
37868                 cn : [
37869                     {
37870                         tag : 'button',
37871                         cls : 'btn btn-default',
37872                         html : '<i class="fa fa-repeat"></i>'
37873                     }
37874                 ]
37875             }
37876         ],
37877         DOCUMENT : [
37878             {
37879                 tag : 'div',
37880                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37881                 action : 'rotate-left',
37882                 cn : [
37883                     {
37884                         tag : 'button',
37885                         cls : 'btn btn-default',
37886                         html : '<i class="fa fa-undo"></i>'
37887                     }
37888                 ]
37889             },
37890             {
37891                 tag : 'div',
37892                 cls : 'btn-group roo-upload-cropbox-download',
37893                 action : 'download',
37894                 cn : [
37895                     {
37896                         tag : 'button',
37897                         cls : 'btn btn-default',
37898                         html : '<i class="fa fa-download"></i>'
37899                     }
37900                 ]
37901             },
37902             {
37903                 tag : 'div',
37904                 cls : 'btn-group roo-upload-cropbox-crop',
37905                 action : 'crop',
37906                 cn : [
37907                     {
37908                         tag : 'button',
37909                         cls : 'btn btn-default',
37910                         html : '<i class="fa fa-crop"></i>'
37911                     }
37912                 ]
37913             },
37914             {
37915                 tag : 'div',
37916                 cls : 'btn-group roo-upload-cropbox-trash',
37917                 action : 'trash',
37918                 cn : [
37919                     {
37920                         tag : 'button',
37921                         cls : 'btn btn-default',
37922                         html : '<i class="fa fa-trash"></i>'
37923                     }
37924                 ]
37925             },
37926             {
37927                 tag : 'div',
37928                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37929                 action : 'rotate-right',
37930                 cn : [
37931                     {
37932                         tag : 'button',
37933                         cls : 'btn btn-default',
37934                         html : '<i class="fa fa-repeat"></i>'
37935                     }
37936                 ]
37937             }
37938         ],
37939         ROTATOR : [
37940             {
37941                 tag : 'div',
37942                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37943                 action : 'rotate-left',
37944                 cn : [
37945                     {
37946                         tag : 'button',
37947                         cls : 'btn btn-default',
37948                         html : '<i class="fa fa-undo"></i>'
37949                     }
37950                 ]
37951             },
37952             {
37953                 tag : 'div',
37954                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37955                 action : 'rotate-right',
37956                 cn : [
37957                     {
37958                         tag : 'button',
37959                         cls : 'btn btn-default',
37960                         html : '<i class="fa fa-repeat"></i>'
37961                     }
37962                 ]
37963             }
37964         ]
37965     }
37966 });
37967
37968 /*
37969 * Licence: LGPL
37970 */
37971
37972 /**
37973  * @class Roo.bootstrap.DocumentManager
37974  * @extends Roo.bootstrap.Component
37975  * Bootstrap DocumentManager class
37976  * @cfg {String} paramName default 'imageUpload'
37977  * @cfg {String} toolTipName default 'filename'
37978  * @cfg {String} method default POST
37979  * @cfg {String} url action url
37980  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37981  * @cfg {Boolean} multiple multiple upload default true
37982  * @cfg {Number} thumbSize default 300
37983  * @cfg {String} fieldLabel
37984  * @cfg {Number} labelWidth default 4
37985  * @cfg {String} labelAlign (left|top) default left
37986  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37987 * @cfg {Number} labellg set the width of label (1-12)
37988  * @cfg {Number} labelmd set the width of label (1-12)
37989  * @cfg {Number} labelsm set the width of label (1-12)
37990  * @cfg {Number} labelxs set the width of label (1-12)
37991  * 
37992  * @constructor
37993  * Create a new DocumentManager
37994  * @param {Object} config The config object
37995  */
37996
37997 Roo.bootstrap.DocumentManager = function(config){
37998     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37999     
38000     this.files = [];
38001     this.delegates = [];
38002     
38003     this.addEvents({
38004         /**
38005          * @event initial
38006          * Fire when initial the DocumentManager
38007          * @param {Roo.bootstrap.DocumentManager} this
38008          */
38009         "initial" : true,
38010         /**
38011          * @event inspect
38012          * inspect selected file
38013          * @param {Roo.bootstrap.DocumentManager} this
38014          * @param {File} file
38015          */
38016         "inspect" : true,
38017         /**
38018          * @event exception
38019          * Fire when xhr load exception
38020          * @param {Roo.bootstrap.DocumentManager} this
38021          * @param {XMLHttpRequest} xhr
38022          */
38023         "exception" : true,
38024         /**
38025          * @event afterupload
38026          * Fire when xhr load exception
38027          * @param {Roo.bootstrap.DocumentManager} this
38028          * @param {XMLHttpRequest} xhr
38029          */
38030         "afterupload" : true,
38031         /**
38032          * @event prepare
38033          * prepare the form data
38034          * @param {Roo.bootstrap.DocumentManager} this
38035          * @param {Object} formData
38036          */
38037         "prepare" : true,
38038         /**
38039          * @event remove
38040          * Fire when remove the file
38041          * @param {Roo.bootstrap.DocumentManager} this
38042          * @param {Object} file
38043          */
38044         "remove" : true,
38045         /**
38046          * @event refresh
38047          * Fire after refresh the file
38048          * @param {Roo.bootstrap.DocumentManager} this
38049          */
38050         "refresh" : true,
38051         /**
38052          * @event click
38053          * Fire after click the image
38054          * @param {Roo.bootstrap.DocumentManager} this
38055          * @param {Object} file
38056          */
38057         "click" : true,
38058         /**
38059          * @event edit
38060          * Fire when upload a image and editable set to true
38061          * @param {Roo.bootstrap.DocumentManager} this
38062          * @param {Object} file
38063          */
38064         "edit" : true,
38065         /**
38066          * @event beforeselectfile
38067          * Fire before select file
38068          * @param {Roo.bootstrap.DocumentManager} this
38069          */
38070         "beforeselectfile" : true,
38071         /**
38072          * @event process
38073          * Fire before process file
38074          * @param {Roo.bootstrap.DocumentManager} this
38075          * @param {Object} file
38076          */
38077         "process" : true,
38078         /**
38079          * @event previewrendered
38080          * Fire when preview rendered
38081          * @param {Roo.bootstrap.DocumentManager} this
38082          * @param {Object} file
38083          */
38084         "previewrendered" : true,
38085         /**
38086          */
38087         "previewResize" : true
38088         
38089     });
38090 };
38091
38092 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
38093     
38094     boxes : 0,
38095     inputName : '',
38096     thumbSize : 300,
38097     multiple : true,
38098     files : false,
38099     method : 'POST',
38100     url : '',
38101     paramName : 'imageUpload',
38102     toolTipName : 'filename',
38103     fieldLabel : '',
38104     labelWidth : 4,
38105     labelAlign : 'left',
38106     editable : true,
38107     delegates : false,
38108     xhr : false, 
38109     
38110     labellg : 0,
38111     labelmd : 0,
38112     labelsm : 0,
38113     labelxs : 0,
38114     
38115     getAutoCreate : function()
38116     {   
38117         var managerWidget = {
38118             tag : 'div',
38119             cls : 'roo-document-manager',
38120             cn : [
38121                 {
38122                     tag : 'input',
38123                     cls : 'roo-document-manager-selector',
38124                     type : 'file'
38125                 },
38126                 {
38127                     tag : 'div',
38128                     cls : 'roo-document-manager-uploader',
38129                     cn : [
38130                         {
38131                             tag : 'div',
38132                             cls : 'roo-document-manager-upload-btn',
38133                             html : '<i class="fa fa-plus"></i>'
38134                         }
38135                     ]
38136                     
38137                 }
38138             ]
38139         };
38140         
38141         var content = [
38142             {
38143                 tag : 'div',
38144                 cls : 'column col-md-12',
38145                 cn : managerWidget
38146             }
38147         ];
38148         
38149         if(this.fieldLabel.length){
38150             
38151             content = [
38152                 {
38153                     tag : 'div',
38154                     cls : 'column col-md-12',
38155                     html : this.fieldLabel
38156                 },
38157                 {
38158                     tag : 'div',
38159                     cls : 'column col-md-12',
38160                     cn : managerWidget
38161                 }
38162             ];
38163
38164             if(this.labelAlign == 'left'){
38165                 content = [
38166                     {
38167                         tag : 'div',
38168                         cls : 'column',
38169                         html : this.fieldLabel
38170                     },
38171                     {
38172                         tag : 'div',
38173                         cls : 'column',
38174                         cn : managerWidget
38175                     }
38176                 ];
38177                 
38178                 if(this.labelWidth > 12){
38179                     content[0].style = "width: " + this.labelWidth + 'px';
38180                 }
38181
38182                 if(this.labelWidth < 13 && this.labelmd == 0){
38183                     this.labelmd = this.labelWidth;
38184                 }
38185
38186                 if(this.labellg > 0){
38187                     content[0].cls += ' col-lg-' + this.labellg;
38188                     content[1].cls += ' col-lg-' + (12 - this.labellg);
38189                 }
38190
38191                 if(this.labelmd > 0){
38192                     content[0].cls += ' col-md-' + this.labelmd;
38193                     content[1].cls += ' col-md-' + (12 - this.labelmd);
38194                 }
38195
38196                 if(this.labelsm > 0){
38197                     content[0].cls += ' col-sm-' + this.labelsm;
38198                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
38199                 }
38200
38201                 if(this.labelxs > 0){
38202                     content[0].cls += ' col-xs-' + this.labelxs;
38203                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
38204                 }
38205                 
38206             }
38207         }
38208         
38209         var cfg = {
38210             tag : 'div',
38211             cls : 'row clearfix',
38212             cn : content
38213         };
38214         
38215         return cfg;
38216         
38217     },
38218     
38219     initEvents : function()
38220     {
38221         this.managerEl = this.el.select('.roo-document-manager', true).first();
38222         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
38223         
38224         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
38225         this.selectorEl.hide();
38226         
38227         if(this.multiple){
38228             this.selectorEl.attr('multiple', 'multiple');
38229         }
38230         
38231         this.selectorEl.on('change', this.onFileSelected, this);
38232         
38233         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
38234         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
38235         
38236         this.uploader.on('click', this.onUploaderClick, this);
38237         
38238         this.renderProgressDialog();
38239         
38240         var _this = this;
38241         
38242         window.addEventListener("resize", function() { _this.refresh(); } );
38243         
38244         this.fireEvent('initial', this);
38245     },
38246     
38247     renderProgressDialog : function()
38248     {
38249         var _this = this;
38250         
38251         this.progressDialog = new Roo.bootstrap.Modal({
38252             cls : 'roo-document-manager-progress-dialog',
38253             allow_close : false,
38254             animate : false,
38255             title : '',
38256             buttons : [
38257                 {
38258                     name  :'cancel',
38259                     weight : 'danger',
38260                     html : 'Cancel'
38261                 }
38262             ], 
38263             listeners : { 
38264                 btnclick : function() {
38265                     _this.uploadCancel();
38266                     this.hide();
38267                 }
38268             }
38269         });
38270          
38271         this.progressDialog.render(Roo.get(document.body));
38272          
38273         this.progress = new Roo.bootstrap.Progress({
38274             cls : 'roo-document-manager-progress',
38275             active : true,
38276             striped : true
38277         });
38278         
38279         this.progress.render(this.progressDialog.getChildContainer());
38280         
38281         this.progressBar = new Roo.bootstrap.ProgressBar({
38282             cls : 'roo-document-manager-progress-bar',
38283             aria_valuenow : 0,
38284             aria_valuemin : 0,
38285             aria_valuemax : 12,
38286             panel : 'success'
38287         });
38288         
38289         this.progressBar.render(this.progress.getChildContainer());
38290     },
38291     
38292     onUploaderClick : function(e)
38293     {
38294         e.preventDefault();
38295      
38296         if(this.fireEvent('beforeselectfile', this) != false){
38297             this.selectorEl.dom.click();
38298         }
38299         
38300     },
38301     
38302     onFileSelected : function(e)
38303     {
38304         e.preventDefault();
38305         
38306         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
38307             return;
38308         }
38309         
38310         Roo.each(this.selectorEl.dom.files, function(file){
38311             if(this.fireEvent('inspect', this, file) != false){
38312                 this.files.push(file);
38313             }
38314         }, this);
38315         
38316         this.queue();
38317         
38318     },
38319     
38320     queue : function()
38321     {
38322         this.selectorEl.dom.value = '';
38323         
38324         if(!this.files || !this.files.length){
38325             return;
38326         }
38327         
38328         if(this.boxes > 0 && this.files.length > this.boxes){
38329             this.files = this.files.slice(0, this.boxes);
38330         }
38331         
38332         this.uploader.show();
38333         
38334         if(this.boxes > 0 && this.files.length > this.boxes - 1){
38335             this.uploader.hide();
38336         }
38337         
38338         var _this = this;
38339         
38340         var files = [];
38341         
38342         var docs = [];
38343         
38344         Roo.each(this.files, function(file){
38345             
38346             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
38347                 var f = this.renderPreview(file);
38348                 files.push(f);
38349                 return;
38350             }
38351             
38352             if(file.type.indexOf('image') != -1){
38353                 this.delegates.push(
38354                     (function(){
38355                         _this.process(file);
38356                     }).createDelegate(this)
38357                 );
38358         
38359                 return;
38360             }
38361             
38362             docs.push(
38363                 (function(){
38364                     _this.process(file);
38365                 }).createDelegate(this)
38366             );
38367             
38368         }, this);
38369         
38370         this.files = files;
38371         
38372         this.delegates = this.delegates.concat(docs);
38373         
38374         if(!this.delegates.length){
38375             this.refresh();
38376             return;
38377         }
38378         
38379         this.progressBar.aria_valuemax = this.delegates.length;
38380         
38381         this.arrange();
38382         
38383         return;
38384     },
38385     
38386     arrange : function()
38387     {
38388         if(!this.delegates.length){
38389             this.progressDialog.hide();
38390             this.refresh();
38391             return;
38392         }
38393         
38394         var delegate = this.delegates.shift();
38395         
38396         this.progressDialog.show();
38397         
38398         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
38399         
38400         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
38401         
38402         delegate();
38403     },
38404     
38405     refresh : function()
38406     {
38407         this.uploader.show();
38408         
38409         if(this.boxes > 0 && this.files.length > this.boxes - 1){
38410             this.uploader.hide();
38411         }
38412         
38413         Roo.isTouch ? this.closable(false) : this.closable(true);
38414         
38415         this.fireEvent('refresh', this);
38416     },
38417     
38418     onRemove : function(e, el, o)
38419     {
38420         e.preventDefault();
38421         
38422         this.fireEvent('remove', this, o);
38423         
38424     },
38425     
38426     remove : function(o)
38427     {
38428         var files = [];
38429         
38430         Roo.each(this.files, function(file){
38431             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
38432                 files.push(file);
38433                 return;
38434             }
38435
38436             o.target.remove();
38437
38438         }, this);
38439         
38440         this.files = files;
38441         
38442         this.refresh();
38443     },
38444     
38445     clear : function()
38446     {
38447         Roo.each(this.files, function(file){
38448             if(!file.target){
38449                 return;
38450             }
38451             
38452             file.target.remove();
38453
38454         }, this);
38455         
38456         this.files = [];
38457         
38458         this.refresh();
38459     },
38460     
38461     onClick : function(e, el, o)
38462     {
38463         e.preventDefault();
38464         
38465         this.fireEvent('click', this, o);
38466         
38467     },
38468     
38469     closable : function(closable)
38470     {
38471         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
38472             
38473             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
38474             
38475             if(closable){
38476                 el.show();
38477                 return;
38478             }
38479             
38480             el.hide();
38481             
38482         }, this);
38483     },
38484     
38485     xhrOnLoad : function(xhr)
38486     {
38487         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38488             el.remove();
38489         }, this);
38490         
38491         if (xhr.readyState !== 4) {
38492             this.arrange();
38493             this.fireEvent('exception', this, xhr);
38494             return;
38495         }
38496
38497         var response = Roo.decode(xhr.responseText);
38498         
38499         if(!response.success){
38500             this.arrange();
38501             this.fireEvent('exception', this, xhr);
38502             return;
38503         }
38504         
38505         var file = this.renderPreview(response.data);
38506         
38507         this.files.push(file);
38508         
38509         this.arrange();
38510         
38511         this.fireEvent('afterupload', this, xhr);
38512         
38513     },
38514     
38515     xhrOnError : function(xhr)
38516     {
38517         Roo.log('xhr on error');
38518         
38519         var response = Roo.decode(xhr.responseText);
38520           
38521         Roo.log(response);
38522         
38523         this.arrange();
38524     },
38525     
38526     process : function(file)
38527     {
38528         if(this.fireEvent('process', this, file) !== false){
38529             if(this.editable && file.type.indexOf('image') != -1){
38530                 this.fireEvent('edit', this, file);
38531                 return;
38532             }
38533
38534             this.uploadStart(file, false);
38535
38536             return;
38537         }
38538         
38539     },
38540     
38541     uploadStart : function(file, crop)
38542     {
38543         this.xhr = new XMLHttpRequest();
38544         
38545         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
38546             this.arrange();
38547             return;
38548         }
38549         
38550         file.xhr = this.xhr;
38551             
38552         this.managerEl.createChild({
38553             tag : 'div',
38554             cls : 'roo-document-manager-loading',
38555             cn : [
38556                 {
38557                     tag : 'div',
38558                     tooltip : file.name,
38559                     cls : 'roo-document-manager-thumb',
38560                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38561                 }
38562             ]
38563
38564         });
38565
38566         this.xhr.open(this.method, this.url, true);
38567         
38568         var headers = {
38569             "Accept": "application/json",
38570             "Cache-Control": "no-cache",
38571             "X-Requested-With": "XMLHttpRequest"
38572         };
38573         
38574         for (var headerName in headers) {
38575             var headerValue = headers[headerName];
38576             if (headerValue) {
38577                 this.xhr.setRequestHeader(headerName, headerValue);
38578             }
38579         }
38580         
38581         var _this = this;
38582         
38583         this.xhr.onload = function()
38584         {
38585             _this.xhrOnLoad(_this.xhr);
38586         }
38587         
38588         this.xhr.onerror = function()
38589         {
38590             _this.xhrOnError(_this.xhr);
38591         }
38592         
38593         var formData = new FormData();
38594
38595         formData.append('returnHTML', 'NO');
38596         
38597         if(crop){
38598             formData.append('crop', crop);
38599         }
38600         
38601         formData.append(this.paramName, file, file.name);
38602         
38603         var options = {
38604             file : file, 
38605             manually : false
38606         };
38607         
38608         if(this.fireEvent('prepare', this, formData, options) != false){
38609             
38610             if(options.manually){
38611                 return;
38612             }
38613             
38614             this.xhr.send(formData);
38615             return;
38616         };
38617         
38618         this.uploadCancel();
38619     },
38620     
38621     uploadCancel : function()
38622     {
38623         if (this.xhr) {
38624             this.xhr.abort();
38625         }
38626         
38627         this.delegates = [];
38628         
38629         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38630             el.remove();
38631         }, this);
38632         
38633         this.arrange();
38634     },
38635     
38636     renderPreview : function(file)
38637     {
38638         if(typeof(file.target) != 'undefined' && file.target){
38639             return file;
38640         }
38641         
38642         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38643         
38644         var previewEl = this.managerEl.createChild({
38645             tag : 'div',
38646             cls : 'roo-document-manager-preview',
38647             cn : [
38648                 {
38649                     tag : 'div',
38650                     tooltip : file[this.toolTipName],
38651                     cls : 'roo-document-manager-thumb',
38652                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38653                 },
38654                 {
38655                     tag : 'button',
38656                     cls : 'close',
38657                     html : '<i class="fa fa-times-circle"></i>'
38658                 }
38659             ]
38660         });
38661
38662         var close = previewEl.select('button.close', true).first();
38663
38664         close.on('click', this.onRemove, this, file);
38665
38666         file.target = previewEl;
38667
38668         var image = previewEl.select('img', true).first();
38669         
38670         var _this = this;
38671         
38672         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38673         
38674         image.on('click', this.onClick, this, file);
38675         
38676         this.fireEvent('previewrendered', this, file);
38677         
38678         return file;
38679         
38680     },
38681     
38682     onPreviewLoad : function(file, image)
38683     {
38684         if(typeof(file.target) == 'undefined' || !file.target){
38685             return;
38686         }
38687         
38688         var width = image.dom.naturalWidth || image.dom.width;
38689         var height = image.dom.naturalHeight || image.dom.height;
38690         
38691         if(!this.previewResize) {
38692             return;
38693         }
38694         
38695         if(width > height){
38696             file.target.addClass('wide');
38697             return;
38698         }
38699         
38700         file.target.addClass('tall');
38701         return;
38702         
38703     },
38704     
38705     uploadFromSource : function(file, crop)
38706     {
38707         this.xhr = new XMLHttpRequest();
38708         
38709         this.managerEl.createChild({
38710             tag : 'div',
38711             cls : 'roo-document-manager-loading',
38712             cn : [
38713                 {
38714                     tag : 'div',
38715                     tooltip : file.name,
38716                     cls : 'roo-document-manager-thumb',
38717                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38718                 }
38719             ]
38720
38721         });
38722
38723         this.xhr.open(this.method, this.url, true);
38724         
38725         var headers = {
38726             "Accept": "application/json",
38727             "Cache-Control": "no-cache",
38728             "X-Requested-With": "XMLHttpRequest"
38729         };
38730         
38731         for (var headerName in headers) {
38732             var headerValue = headers[headerName];
38733             if (headerValue) {
38734                 this.xhr.setRequestHeader(headerName, headerValue);
38735             }
38736         }
38737         
38738         var _this = this;
38739         
38740         this.xhr.onload = function()
38741         {
38742             _this.xhrOnLoad(_this.xhr);
38743         }
38744         
38745         this.xhr.onerror = function()
38746         {
38747             _this.xhrOnError(_this.xhr);
38748         }
38749         
38750         var formData = new FormData();
38751
38752         formData.append('returnHTML', 'NO');
38753         
38754         formData.append('crop', crop);
38755         
38756         if(typeof(file.filename) != 'undefined'){
38757             formData.append('filename', file.filename);
38758         }
38759         
38760         if(typeof(file.mimetype) != 'undefined'){
38761             formData.append('mimetype', file.mimetype);
38762         }
38763         
38764         Roo.log(formData);
38765         
38766         if(this.fireEvent('prepare', this, formData) != false){
38767             this.xhr.send(formData);
38768         };
38769     }
38770 });
38771
38772 /*
38773 * Licence: LGPL
38774 */
38775
38776 /**
38777  * @class Roo.bootstrap.DocumentViewer
38778  * @extends Roo.bootstrap.Component
38779  * Bootstrap DocumentViewer class
38780  * @cfg {Boolean} showDownload (true|false) show download button (default true)
38781  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38782  * 
38783  * @constructor
38784  * Create a new DocumentViewer
38785  * @param {Object} config The config object
38786  */
38787
38788 Roo.bootstrap.DocumentViewer = function(config){
38789     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38790     
38791     this.addEvents({
38792         /**
38793          * @event initial
38794          * Fire after initEvent
38795          * @param {Roo.bootstrap.DocumentViewer} this
38796          */
38797         "initial" : true,
38798         /**
38799          * @event click
38800          * Fire after click
38801          * @param {Roo.bootstrap.DocumentViewer} this
38802          */
38803         "click" : true,
38804         /**
38805          * @event download
38806          * Fire after download button
38807          * @param {Roo.bootstrap.DocumentViewer} this
38808          */
38809         "download" : true,
38810         /**
38811          * @event trash
38812          * Fire after trash button
38813          * @param {Roo.bootstrap.DocumentViewer} this
38814          */
38815         "trash" : true
38816         
38817     });
38818 };
38819
38820 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
38821     
38822     showDownload : true,
38823     
38824     showTrash : true,
38825     
38826     getAutoCreate : function()
38827     {
38828         var cfg = {
38829             tag : 'div',
38830             cls : 'roo-document-viewer',
38831             cn : [
38832                 {
38833                     tag : 'div',
38834                     cls : 'roo-document-viewer-body',
38835                     cn : [
38836                         {
38837                             tag : 'div',
38838                             cls : 'roo-document-viewer-thumb',
38839                             cn : [
38840                                 {
38841                                     tag : 'img',
38842                                     cls : 'roo-document-viewer-image'
38843                                 }
38844                             ]
38845                         }
38846                     ]
38847                 },
38848                 {
38849                     tag : 'div',
38850                     cls : 'roo-document-viewer-footer',
38851                     cn : {
38852                         tag : 'div',
38853                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38854                         cn : [
38855                             {
38856                                 tag : 'div',
38857                                 cls : 'btn-group roo-document-viewer-download',
38858                                 cn : [
38859                                     {
38860                                         tag : 'button',
38861                                         cls : 'btn btn-default',
38862                                         html : '<i class="fa fa-download"></i>'
38863                                     }
38864                                 ]
38865                             },
38866                             {
38867                                 tag : 'div',
38868                                 cls : 'btn-group roo-document-viewer-trash',
38869                                 cn : [
38870                                     {
38871                                         tag : 'button',
38872                                         cls : 'btn btn-default',
38873                                         html : '<i class="fa fa-trash"></i>'
38874                                     }
38875                                 ]
38876                             }
38877                         ]
38878                     }
38879                 }
38880             ]
38881         };
38882         
38883         return cfg;
38884     },
38885     
38886     initEvents : function()
38887     {
38888         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38889         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38890         
38891         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38892         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38893         
38894         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38895         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38896         
38897         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38898         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38899         
38900         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38901         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38902         
38903         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38904         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38905         
38906         this.bodyEl.on('click', this.onClick, this);
38907         this.downloadBtn.on('click', this.onDownload, this);
38908         this.trashBtn.on('click', this.onTrash, this);
38909         
38910         this.downloadBtn.hide();
38911         this.trashBtn.hide();
38912         
38913         if(this.showDownload){
38914             this.downloadBtn.show();
38915         }
38916         
38917         if(this.showTrash){
38918             this.trashBtn.show();
38919         }
38920         
38921         if(!this.showDownload && !this.showTrash) {
38922             this.footerEl.hide();
38923         }
38924         
38925     },
38926     
38927     initial : function()
38928     {
38929         this.fireEvent('initial', this);
38930         
38931     },
38932     
38933     onClick : function(e)
38934     {
38935         e.preventDefault();
38936         
38937         this.fireEvent('click', this);
38938     },
38939     
38940     onDownload : function(e)
38941     {
38942         e.preventDefault();
38943         
38944         this.fireEvent('download', this);
38945     },
38946     
38947     onTrash : function(e)
38948     {
38949         e.preventDefault();
38950         
38951         this.fireEvent('trash', this);
38952     }
38953     
38954 });
38955 /*
38956  * - LGPL
38957  *
38958  * FieldLabel
38959  * 
38960  */
38961
38962 /**
38963  * @class Roo.bootstrap.form.FieldLabel
38964  * @extends Roo.bootstrap.Component
38965  * Bootstrap FieldLabel class
38966  * @cfg {String} html contents of the element
38967  * @cfg {String} tag tag of the element default label
38968  * @cfg {String} cls class of the element
38969  * @cfg {String} target label target 
38970  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38971  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38972  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38973  * @cfg {String} iconTooltip default "This field is required"
38974  * @cfg {String} indicatorpos (left|right) default left
38975  * 
38976  * @constructor
38977  * Create a new FieldLabel
38978  * @param {Object} config The config object
38979  */
38980
38981 Roo.bootstrap.form.FieldLabel = function(config){
38982     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38983     
38984     this.addEvents({
38985             /**
38986              * @event invalid
38987              * Fires after the field has been marked as invalid.
38988              * @param {Roo.form.FieldLabel} this
38989              * @param {String} msg The validation message
38990              */
38991             invalid : true,
38992             /**
38993              * @event valid
38994              * Fires after the field has been validated with no errors.
38995              * @param {Roo.form.FieldLabel} this
38996              */
38997             valid : true
38998         });
38999 };
39000
39001 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
39002     
39003     tag: 'label',
39004     cls: '',
39005     html: '',
39006     target: '',
39007     allowBlank : true,
39008     invalidClass : 'has-warning',
39009     validClass : 'has-success',
39010     iconTooltip : 'This field is required',
39011     indicatorpos : 'left',
39012     
39013     getAutoCreate : function(){
39014         
39015         var cls = "";
39016         if (!this.allowBlank) {
39017             cls  = "visible";
39018         }
39019         
39020         var cfg = {
39021             tag : this.tag,
39022             cls : 'roo-bootstrap-field-label ' + this.cls,
39023             for : this.target,
39024             cn : [
39025                 {
39026                     tag : 'i',
39027                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
39028                     tooltip : this.iconTooltip
39029                 },
39030                 {
39031                     tag : 'span',
39032                     html : this.html
39033                 }
39034             ] 
39035         };
39036         
39037         if(this.indicatorpos == 'right'){
39038             var cfg = {
39039                 tag : this.tag,
39040                 cls : 'roo-bootstrap-field-label ' + this.cls,
39041                 for : this.target,
39042                 cn : [
39043                     {
39044                         tag : 'span',
39045                         html : this.html
39046                     },
39047                     {
39048                         tag : 'i',
39049                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
39050                         tooltip : this.iconTooltip
39051                     }
39052                 ] 
39053             };
39054         }
39055         
39056         return cfg;
39057     },
39058     
39059     initEvents: function() 
39060     {
39061         Roo.bootstrap.Element.superclass.initEvents.call(this);
39062         
39063         this.indicator = this.indicatorEl();
39064         
39065         if(this.indicator){
39066             this.indicator.removeClass('visible');
39067             this.indicator.addClass('invisible');
39068         }
39069         
39070         Roo.bootstrap.form.FieldLabel.register(this);
39071     },
39072     
39073     indicatorEl : function()
39074     {
39075         var indicator = this.el.select('i.roo-required-indicator',true).first();
39076         
39077         if(!indicator){
39078             return false;
39079         }
39080         
39081         return indicator;
39082         
39083     },
39084     
39085     /**
39086      * Mark this field as valid
39087      */
39088     markValid : function()
39089     {
39090         if(this.indicator){
39091             this.indicator.removeClass('visible');
39092             this.indicator.addClass('invisible');
39093         }
39094         if (Roo.bootstrap.version == 3) {
39095             this.el.removeClass(this.invalidClass);
39096             this.el.addClass(this.validClass);
39097         } else {
39098             this.el.removeClass('is-invalid');
39099             this.el.addClass('is-valid');
39100         }
39101         
39102         
39103         this.fireEvent('valid', this);
39104     },
39105     
39106     /**
39107      * Mark this field as invalid
39108      * @param {String} msg The validation message
39109      */
39110     markInvalid : function(msg)
39111     {
39112         if(this.indicator){
39113             this.indicator.removeClass('invisible');
39114             this.indicator.addClass('visible');
39115         }
39116           if (Roo.bootstrap.version == 3) {
39117             this.el.removeClass(this.validClass);
39118             this.el.addClass(this.invalidClass);
39119         } else {
39120             this.el.removeClass('is-valid');
39121             this.el.addClass('is-invalid');
39122         }
39123         
39124         
39125         this.fireEvent('invalid', this, msg);
39126     }
39127     
39128    
39129 });
39130
39131 Roo.apply(Roo.bootstrap.form.FieldLabel, {
39132     
39133     groups: {},
39134     
39135      /**
39136     * register a FieldLabel Group
39137     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
39138     */
39139     register : function(label)
39140     {
39141         if(this.groups.hasOwnProperty(label.target)){
39142             return;
39143         }
39144      
39145         this.groups[label.target] = label;
39146         
39147     },
39148     /**
39149     * fetch a FieldLabel Group based on the target
39150     * @param {string} target
39151     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
39152     */
39153     get: function(target) {
39154         if (typeof(this.groups[target]) == 'undefined') {
39155             return false;
39156         }
39157         
39158         return this.groups[target] ;
39159     }
39160 });
39161
39162  
39163
39164  /*
39165  * - LGPL
39166  *
39167  * page DateSplitField.
39168  * 
39169  */
39170
39171
39172 /**
39173  * @class Roo.bootstrap.form.DateSplitField
39174  * @extends Roo.bootstrap.Component
39175  * Bootstrap DateSplitField class
39176  * @cfg {string} fieldLabel - the label associated
39177  * @cfg {Number} labelWidth set the width of label (0-12)
39178  * @cfg {String} labelAlign (top|left)
39179  * @cfg {Boolean} dayAllowBlank (true|false) default false
39180  * @cfg {Boolean} monthAllowBlank (true|false) default false
39181  * @cfg {Boolean} yearAllowBlank (true|false) default false
39182  * @cfg {string} dayPlaceholder 
39183  * @cfg {string} monthPlaceholder
39184  * @cfg {string} yearPlaceholder
39185  * @cfg {string} dayFormat default 'd'
39186  * @cfg {string} monthFormat default 'm'
39187  * @cfg {string} yearFormat default 'Y'
39188  * @cfg {Number} labellg set the width of label (1-12)
39189  * @cfg {Number} labelmd set the width of label (1-12)
39190  * @cfg {Number} labelsm set the width of label (1-12)
39191  * @cfg {Number} labelxs set the width of label (1-12)
39192
39193  *     
39194  * @constructor
39195  * Create a new DateSplitField
39196  * @param {Object} config The config object
39197  */
39198
39199 Roo.bootstrap.form.DateSplitField = function(config){
39200     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
39201     
39202     this.addEvents({
39203         // raw events
39204          /**
39205          * @event years
39206          * getting the data of years
39207          * @param {Roo.bootstrap.form.DateSplitField} this
39208          * @param {Object} years
39209          */
39210         "years" : true,
39211         /**
39212          * @event days
39213          * getting the data of days
39214          * @param {Roo.bootstrap.form.DateSplitField} this
39215          * @param {Object} days
39216          */
39217         "days" : true,
39218         /**
39219          * @event invalid
39220          * Fires after the field has been marked as invalid.
39221          * @param {Roo.form.Field} this
39222          * @param {String} msg The validation message
39223          */
39224         invalid : true,
39225        /**
39226          * @event valid
39227          * Fires after the field has been validated with no errors.
39228          * @param {Roo.form.Field} this
39229          */
39230         valid : true
39231     });
39232 };
39233
39234 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
39235     
39236     fieldLabel : '',
39237     labelAlign : 'top',
39238     labelWidth : 3,
39239     dayAllowBlank : false,
39240     monthAllowBlank : false,
39241     yearAllowBlank : false,
39242     dayPlaceholder : '',
39243     monthPlaceholder : '',
39244     yearPlaceholder : '',
39245     dayFormat : 'd',
39246     monthFormat : 'm',
39247     yearFormat : 'Y',
39248     isFormField : true,
39249     labellg : 0,
39250     labelmd : 0,
39251     labelsm : 0,
39252     labelxs : 0,
39253     
39254     getAutoCreate : function()
39255     {
39256         var cfg = {
39257             tag : 'div',
39258             cls : 'row roo-date-split-field-group',
39259             cn : [
39260                 {
39261                     tag : 'input',
39262                     type : 'hidden',
39263                     cls : 'form-hidden-field roo-date-split-field-group-value',
39264                     name : this.name
39265                 }
39266             ]
39267         };
39268         
39269         var labelCls = 'col-md-12';
39270         var contentCls = 'col-md-4';
39271         
39272         if(this.fieldLabel){
39273             
39274             var label = {
39275                 tag : 'div',
39276                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
39277                 cn : [
39278                     {
39279                         tag : 'label',
39280                         html : this.fieldLabel
39281                     }
39282                 ]
39283             };
39284             
39285             if(this.labelAlign == 'left'){
39286             
39287                 if(this.labelWidth > 12){
39288                     label.style = "width: " + this.labelWidth + 'px';
39289                 }
39290
39291                 if(this.labelWidth < 13 && this.labelmd == 0){
39292                     this.labelmd = this.labelWidth;
39293                 }
39294
39295                 if(this.labellg > 0){
39296                     labelCls = ' col-lg-' + this.labellg;
39297                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
39298                 }
39299
39300                 if(this.labelmd > 0){
39301                     labelCls = ' col-md-' + this.labelmd;
39302                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
39303                 }
39304
39305                 if(this.labelsm > 0){
39306                     labelCls = ' col-sm-' + this.labelsm;
39307                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
39308                 }
39309
39310                 if(this.labelxs > 0){
39311                     labelCls = ' col-xs-' + this.labelxs;
39312                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
39313                 }
39314             }
39315             
39316             label.cls += ' ' + labelCls;
39317             
39318             cfg.cn.push(label);
39319         }
39320         
39321         Roo.each(['day', 'month', 'year'], function(t){
39322             cfg.cn.push({
39323                 tag : 'div',
39324                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
39325             });
39326         }, this);
39327         
39328         return cfg;
39329     },
39330     
39331     inputEl: function ()
39332     {
39333         return this.el.select('.roo-date-split-field-group-value', true).first();
39334     },
39335     
39336     onRender : function(ct, position) 
39337     {
39338         var _this = this;
39339         
39340         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
39341         
39342         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
39343         
39344         this.dayField = new Roo.bootstrap.form.ComboBox({
39345             allowBlank : this.dayAllowBlank,
39346             alwaysQuery : true,
39347             displayField : 'value',
39348             editable : false,
39349             fieldLabel : '',
39350             forceSelection : true,
39351             mode : 'local',
39352             placeholder : this.dayPlaceholder,
39353             selectOnFocus : true,
39354             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
39355             triggerAction : 'all',
39356             typeAhead : true,
39357             valueField : 'value',
39358             store : new Roo.data.SimpleStore({
39359                 data : (function() {    
39360                     var days = [];
39361                     _this.fireEvent('days', _this, days);
39362                     return days;
39363                 })(),
39364                 fields : [ 'value' ]
39365             }),
39366             listeners : {
39367                 select : function (_self, record, index)
39368                 {
39369                     _this.setValue(_this.getValue());
39370                 }
39371             }
39372         });
39373
39374         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
39375         
39376         this.monthField = new Roo.bootstrap.form.MonthField({
39377             after : '<i class=\"fa fa-calendar\"></i>',
39378             allowBlank : this.monthAllowBlank,
39379             placeholder : this.monthPlaceholder,
39380             readOnly : true,
39381             listeners : {
39382                 render : function (_self)
39383                 {
39384                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
39385                         e.preventDefault();
39386                         _self.focus();
39387                     });
39388                 },
39389                 select : function (_self, oldvalue, newvalue)
39390                 {
39391                     _this.setValue(_this.getValue());
39392                 }
39393             }
39394         });
39395         
39396         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
39397         
39398         this.yearField = new Roo.bootstrap.form.ComboBox({
39399             allowBlank : this.yearAllowBlank,
39400             alwaysQuery : true,
39401             displayField : 'value',
39402             editable : false,
39403             fieldLabel : '',
39404             forceSelection : true,
39405             mode : 'local',
39406             placeholder : this.yearPlaceholder,
39407             selectOnFocus : true,
39408             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
39409             triggerAction : 'all',
39410             typeAhead : true,
39411             valueField : 'value',
39412             store : new Roo.data.SimpleStore({
39413                 data : (function() {
39414                     var years = [];
39415                     _this.fireEvent('years', _this, years);
39416                     return years;
39417                 })(),
39418                 fields : [ 'value' ]
39419             }),
39420             listeners : {
39421                 select : function (_self, record, index)
39422                 {
39423                     _this.setValue(_this.getValue());
39424                 }
39425             }
39426         });
39427
39428         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
39429     },
39430     
39431     setValue : function(v, format)
39432     {
39433         this.inputEl.dom.value = v;
39434         
39435         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
39436         
39437         var d = Date.parseDate(v, f);
39438         
39439         if(!d){
39440             this.validate();
39441             return;
39442         }
39443         
39444         this.setDay(d.format(this.dayFormat));
39445         this.setMonth(d.format(this.monthFormat));
39446         this.setYear(d.format(this.yearFormat));
39447         
39448         this.validate();
39449         
39450         return;
39451     },
39452     
39453     setDay : function(v)
39454     {
39455         this.dayField.setValue(v);
39456         this.inputEl.dom.value = this.getValue();
39457         this.validate();
39458         return;
39459     },
39460     
39461     setMonth : function(v)
39462     {
39463         this.monthField.setValue(v, true);
39464         this.inputEl.dom.value = this.getValue();
39465         this.validate();
39466         return;
39467     },
39468     
39469     setYear : function(v)
39470     {
39471         this.yearField.setValue(v);
39472         this.inputEl.dom.value = this.getValue();
39473         this.validate();
39474         return;
39475     },
39476     
39477     getDay : function()
39478     {
39479         return this.dayField.getValue();
39480     },
39481     
39482     getMonth : function()
39483     {
39484         return this.monthField.getValue();
39485     },
39486     
39487     getYear : function()
39488     {
39489         return this.yearField.getValue();
39490     },
39491     
39492     getValue : function()
39493     {
39494         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
39495         
39496         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
39497         
39498         return date;
39499     },
39500     
39501     reset : function()
39502     {
39503         this.setDay('');
39504         this.setMonth('');
39505         this.setYear('');
39506         this.inputEl.dom.value = '';
39507         this.validate();
39508         return;
39509     },
39510     
39511     validate : function()
39512     {
39513         var d = this.dayField.validate();
39514         var m = this.monthField.validate();
39515         var y = this.yearField.validate();
39516         
39517         var valid = true;
39518         
39519         if(
39520                 (!this.dayAllowBlank && !d) ||
39521                 (!this.monthAllowBlank && !m) ||
39522                 (!this.yearAllowBlank && !y)
39523         ){
39524             valid = false;
39525         }
39526         
39527         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
39528             return valid;
39529         }
39530         
39531         if(valid){
39532             this.markValid();
39533             return valid;
39534         }
39535         
39536         this.markInvalid();
39537         
39538         return valid;
39539     },
39540     
39541     markValid : function()
39542     {
39543         
39544         var label = this.el.select('label', true).first();
39545         var icon = this.el.select('i.fa-star', true).first();
39546
39547         if(label && icon){
39548             icon.remove();
39549         }
39550         
39551         this.fireEvent('valid', this);
39552     },
39553     
39554      /**
39555      * Mark this field as invalid
39556      * @param {String} msg The validation message
39557      */
39558     markInvalid : function(msg)
39559     {
39560         
39561         var label = this.el.select('label', true).first();
39562         var icon = this.el.select('i.fa-star', true).first();
39563
39564         if(label && !icon){
39565             this.el.select('.roo-date-split-field-label', true).createChild({
39566                 tag : 'i',
39567                 cls : 'text-danger fa fa-lg fa-star',
39568                 tooltip : 'This field is required',
39569                 style : 'margin-right:5px;'
39570             }, label, true);
39571         }
39572         
39573         this.fireEvent('invalid', this, msg);
39574     },
39575     
39576     clearInvalid : function()
39577     {
39578         var label = this.el.select('label', true).first();
39579         var icon = this.el.select('i.fa-star', true).first();
39580
39581         if(label && icon){
39582             icon.remove();
39583         }
39584         
39585         this.fireEvent('valid', this);
39586     },
39587     
39588     getName: function()
39589     {
39590         return this.name;
39591     }
39592     
39593 });
39594
39595  
39596
39597 /**
39598  * @class Roo.bootstrap.LayoutMasonry
39599  * @extends Roo.bootstrap.Component
39600  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39601  * Bootstrap Layout Masonry class
39602  *
39603  * This is based on 
39604  * http://masonry.desandro.com
39605  *
39606  * The idea is to render all the bricks based on vertical width...
39607  *
39608  * The original code extends 'outlayer' - we might need to use that....
39609
39610  * @constructor
39611  * Create a new Element
39612  * @param {Object} config The config object
39613  */
39614
39615 Roo.bootstrap.LayoutMasonry = function(config){
39616     
39617     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39618     
39619     this.bricks = [];
39620     
39621     Roo.bootstrap.LayoutMasonry.register(this);
39622     
39623     this.addEvents({
39624         // raw events
39625         /**
39626          * @event layout
39627          * Fire after layout the items
39628          * @param {Roo.bootstrap.LayoutMasonry} this
39629          * @param {Roo.EventObject} e
39630          */
39631         "layout" : true
39632     });
39633     
39634 };
39635
39636 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
39637     
39638     /**
39639      * @cfg {Boolean} isLayoutInstant = no animation?
39640      */   
39641     isLayoutInstant : false, // needed?
39642    
39643     /**
39644      * @cfg {Number} boxWidth  width of the columns
39645      */   
39646     boxWidth : 450,
39647     
39648       /**
39649      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
39650      */   
39651     boxHeight : 0,
39652     
39653     /**
39654      * @cfg {Number} padWidth padding below box..
39655      */   
39656     padWidth : 10, 
39657     
39658     /**
39659      * @cfg {Number} gutter gutter width..
39660      */   
39661     gutter : 10,
39662     
39663      /**
39664      * @cfg {Number} maxCols maximum number of columns
39665      */   
39666     
39667     maxCols: 0,
39668     
39669     /**
39670      * @cfg {Boolean} isAutoInitial defalut true
39671      */   
39672     isAutoInitial : true, 
39673     
39674     containerWidth: 0,
39675     
39676     /**
39677      * @cfg {Boolean} isHorizontal defalut false
39678      */   
39679     isHorizontal : false, 
39680
39681     currentSize : null,
39682     
39683     tag: 'div',
39684     
39685     cls: '',
39686     
39687     bricks: null, //CompositeElement
39688     
39689     cols : 1,
39690     
39691     _isLayoutInited : false,
39692     
39693 //    isAlternative : false, // only use for vertical layout...
39694     
39695     /**
39696      * @cfg {Number} alternativePadWidth padding below box..
39697      */   
39698     alternativePadWidth : 50,
39699     
39700     selectedBrick : [],
39701     
39702     getAutoCreate : function(){
39703         
39704         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39705         
39706         var cfg = {
39707             tag: this.tag,
39708             cls: 'blog-masonary-wrapper ' + this.cls,
39709             cn : {
39710                 cls : 'mas-boxes masonary'
39711             }
39712         };
39713         
39714         return cfg;
39715     },
39716     
39717     getChildContainer: function( )
39718     {
39719         if (this.boxesEl) {
39720             return this.boxesEl;
39721         }
39722         
39723         this.boxesEl = this.el.select('.mas-boxes').first();
39724         
39725         return this.boxesEl;
39726     },
39727     
39728     
39729     initEvents : function()
39730     {
39731         var _this = this;
39732         
39733         if(this.isAutoInitial){
39734             Roo.log('hook children rendered');
39735             this.on('childrenrendered', function() {
39736                 Roo.log('children rendered');
39737                 _this.initial();
39738             } ,this);
39739         }
39740     },
39741     
39742     initial : function()
39743     {
39744         this.selectedBrick = [];
39745         
39746         this.currentSize = this.el.getBox(true);
39747         
39748         Roo.EventManager.onWindowResize(this.resize, this); 
39749
39750         if(!this.isAutoInitial){
39751             this.layout();
39752             return;
39753         }
39754         
39755         this.layout();
39756         
39757         return;
39758         //this.layout.defer(500,this);
39759         
39760     },
39761     
39762     resize : function()
39763     {
39764         var cs = this.el.getBox(true);
39765         
39766         if (
39767                 this.currentSize.width == cs.width && 
39768                 this.currentSize.x == cs.x && 
39769                 this.currentSize.height == cs.height && 
39770                 this.currentSize.y == cs.y 
39771         ) {
39772             Roo.log("no change in with or X or Y");
39773             return;
39774         }
39775         
39776         this.currentSize = cs;
39777         
39778         this.layout();
39779         
39780     },
39781     
39782     layout : function()
39783     {   
39784         this._resetLayout();
39785         
39786         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39787         
39788         this.layoutItems( isInstant );
39789       
39790         this._isLayoutInited = true;
39791         
39792         this.fireEvent('layout', this);
39793         
39794     },
39795     
39796     _resetLayout : function()
39797     {
39798         if(this.isHorizontal){
39799             this.horizontalMeasureColumns();
39800             return;
39801         }
39802         
39803         this.verticalMeasureColumns();
39804         
39805     },
39806     
39807     verticalMeasureColumns : function()
39808     {
39809         this.getContainerWidth();
39810         
39811 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39812 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
39813 //            return;
39814 //        }
39815         
39816         var boxWidth = this.boxWidth + this.padWidth;
39817         
39818         if(this.containerWidth < this.boxWidth){
39819             boxWidth = this.containerWidth
39820         }
39821         
39822         var containerWidth = this.containerWidth;
39823         
39824         var cols = Math.floor(containerWidth / boxWidth);
39825         
39826         this.cols = Math.max( cols, 1 );
39827         
39828         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39829         
39830         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39831         
39832         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39833         
39834         this.colWidth = boxWidth + avail - this.padWidth;
39835         
39836         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39837         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
39838     },
39839     
39840     horizontalMeasureColumns : function()
39841     {
39842         this.getContainerWidth();
39843         
39844         var boxWidth = this.boxWidth;
39845         
39846         if(this.containerWidth < boxWidth){
39847             boxWidth = this.containerWidth;
39848         }
39849         
39850         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39851         
39852         this.el.setHeight(boxWidth);
39853         
39854     },
39855     
39856     getContainerWidth : function()
39857     {
39858         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39859     },
39860     
39861     layoutItems : function( isInstant )
39862     {
39863         Roo.log(this.bricks);
39864         
39865         var items = Roo.apply([], this.bricks);
39866         
39867         if(this.isHorizontal){
39868             this._horizontalLayoutItems( items , isInstant );
39869             return;
39870         }
39871         
39872 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39873 //            this._verticalAlternativeLayoutItems( items , isInstant );
39874 //            return;
39875 //        }
39876         
39877         this._verticalLayoutItems( items , isInstant );
39878         
39879     },
39880     
39881     _verticalLayoutItems : function ( items , isInstant)
39882     {
39883         if ( !items || !items.length ) {
39884             return;
39885         }
39886         
39887         var standard = [
39888             ['xs', 'xs', 'xs', 'tall'],
39889             ['xs', 'xs', 'tall'],
39890             ['xs', 'xs', 'sm'],
39891             ['xs', 'xs', 'xs'],
39892             ['xs', 'tall'],
39893             ['xs', 'sm'],
39894             ['xs', 'xs'],
39895             ['xs'],
39896             
39897             ['sm', 'xs', 'xs'],
39898             ['sm', 'xs'],
39899             ['sm'],
39900             
39901             ['tall', 'xs', 'xs', 'xs'],
39902             ['tall', 'xs', 'xs'],
39903             ['tall', 'xs'],
39904             ['tall']
39905             
39906         ];
39907         
39908         var queue = [];
39909         
39910         var boxes = [];
39911         
39912         var box = [];
39913         
39914         Roo.each(items, function(item, k){
39915             
39916             switch (item.size) {
39917                 // these layouts take up a full box,
39918                 case 'md' :
39919                 case 'md-left' :
39920                 case 'md-right' :
39921                 case 'wide' :
39922                     
39923                     if(box.length){
39924                         boxes.push(box);
39925                         box = [];
39926                     }
39927                     
39928                     boxes.push([item]);
39929                     
39930                     break;
39931                     
39932                 case 'xs' :
39933                 case 'sm' :
39934                 case 'tall' :
39935                     
39936                     box.push(item);
39937                     
39938                     break;
39939                 default :
39940                     break;
39941                     
39942             }
39943             
39944         }, this);
39945         
39946         if(box.length){
39947             boxes.push(box);
39948             box = [];
39949         }
39950         
39951         var filterPattern = function(box, length)
39952         {
39953             if(!box.length){
39954                 return;
39955             }
39956             
39957             var match = false;
39958             
39959             var pattern = box.slice(0, length);
39960             
39961             var format = [];
39962             
39963             Roo.each(pattern, function(i){
39964                 format.push(i.size);
39965             }, this);
39966             
39967             Roo.each(standard, function(s){
39968                 
39969                 if(String(s) != String(format)){
39970                     return;
39971                 }
39972                 
39973                 match = true;
39974                 return false;
39975                 
39976             }, this);
39977             
39978             if(!match && length == 1){
39979                 return;
39980             }
39981             
39982             if(!match){
39983                 filterPattern(box, length - 1);
39984                 return;
39985             }
39986                 
39987             queue.push(pattern);
39988
39989             box = box.slice(length, box.length);
39990
39991             filterPattern(box, 4);
39992
39993             return;
39994             
39995         }
39996         
39997         Roo.each(boxes, function(box, k){
39998             
39999             if(!box.length){
40000                 return;
40001             }
40002             
40003             if(box.length == 1){
40004                 queue.push(box);
40005                 return;
40006             }
40007             
40008             filterPattern(box, 4);
40009             
40010         }, this);
40011         
40012         this._processVerticalLayoutQueue( queue, isInstant );
40013         
40014     },
40015     
40016 //    _verticalAlternativeLayoutItems : function( items , isInstant )
40017 //    {
40018 //        if ( !items || !items.length ) {
40019 //            return;
40020 //        }
40021 //
40022 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
40023 //        
40024 //    },
40025     
40026     _horizontalLayoutItems : function ( items , isInstant)
40027     {
40028         if ( !items || !items.length || items.length < 3) {
40029             return;
40030         }
40031         
40032         items.reverse();
40033         
40034         var eItems = items.slice(0, 3);
40035         
40036         items = items.slice(3, items.length);
40037         
40038         var standard = [
40039             ['xs', 'xs', 'xs', 'wide'],
40040             ['xs', 'xs', 'wide'],
40041             ['xs', 'xs', 'sm'],
40042             ['xs', 'xs', 'xs'],
40043             ['xs', 'wide'],
40044             ['xs', 'sm'],
40045             ['xs', 'xs'],
40046             ['xs'],
40047             
40048             ['sm', 'xs', 'xs'],
40049             ['sm', 'xs'],
40050             ['sm'],
40051             
40052             ['wide', 'xs', 'xs', 'xs'],
40053             ['wide', 'xs', 'xs'],
40054             ['wide', 'xs'],
40055             ['wide'],
40056             
40057             ['wide-thin']
40058         ];
40059         
40060         var queue = [];
40061         
40062         var boxes = [];
40063         
40064         var box = [];
40065         
40066         Roo.each(items, function(item, k){
40067             
40068             switch (item.size) {
40069                 case 'md' :
40070                 case 'md-left' :
40071                 case 'md-right' :
40072                 case 'tall' :
40073                     
40074                     if(box.length){
40075                         boxes.push(box);
40076                         box = [];
40077                     }
40078                     
40079                     boxes.push([item]);
40080                     
40081                     break;
40082                     
40083                 case 'xs' :
40084                 case 'sm' :
40085                 case 'wide' :
40086                 case 'wide-thin' :
40087                     
40088                     box.push(item);
40089                     
40090                     break;
40091                 default :
40092                     break;
40093                     
40094             }
40095             
40096         }, this);
40097         
40098         if(box.length){
40099             boxes.push(box);
40100             box = [];
40101         }
40102         
40103         var filterPattern = function(box, length)
40104         {
40105             if(!box.length){
40106                 return;
40107             }
40108             
40109             var match = false;
40110             
40111             var pattern = box.slice(0, length);
40112             
40113             var format = [];
40114             
40115             Roo.each(pattern, function(i){
40116                 format.push(i.size);
40117             }, this);
40118             
40119             Roo.each(standard, function(s){
40120                 
40121                 if(String(s) != String(format)){
40122                     return;
40123                 }
40124                 
40125                 match = true;
40126                 return false;
40127                 
40128             }, this);
40129             
40130             if(!match && length == 1){
40131                 return;
40132             }
40133             
40134             if(!match){
40135                 filterPattern(box, length - 1);
40136                 return;
40137             }
40138                 
40139             queue.push(pattern);
40140
40141             box = box.slice(length, box.length);
40142
40143             filterPattern(box, 4);
40144
40145             return;
40146             
40147         }
40148         
40149         Roo.each(boxes, function(box, k){
40150             
40151             if(!box.length){
40152                 return;
40153             }
40154             
40155             if(box.length == 1){
40156                 queue.push(box);
40157                 return;
40158             }
40159             
40160             filterPattern(box, 4);
40161             
40162         }, this);
40163         
40164         
40165         var prune = [];
40166         
40167         var pos = this.el.getBox(true);
40168         
40169         var minX = pos.x;
40170         
40171         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
40172         
40173         var hit_end = false;
40174         
40175         Roo.each(queue, function(box){
40176             
40177             if(hit_end){
40178                 
40179                 Roo.each(box, function(b){
40180                 
40181                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
40182                     b.el.hide();
40183
40184                 }, this);
40185
40186                 return;
40187             }
40188             
40189             var mx = 0;
40190             
40191             Roo.each(box, function(b){
40192                 
40193                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
40194                 b.el.show();
40195
40196                 mx = Math.max(mx, b.x);
40197                 
40198             }, this);
40199             
40200             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
40201             
40202             if(maxX < minX){
40203                 
40204                 Roo.each(box, function(b){
40205                 
40206                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
40207                     b.el.hide();
40208                     
40209                 }, this);
40210                 
40211                 hit_end = true;
40212                 
40213                 return;
40214             }
40215             
40216             prune.push(box);
40217             
40218         }, this);
40219         
40220         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
40221     },
40222     
40223     /** Sets position of item in DOM
40224     * @param {Element} item
40225     * @param {Number} x - horizontal position
40226     * @param {Number} y - vertical position
40227     * @param {Boolean} isInstant - disables transitions
40228     */
40229     _processVerticalLayoutQueue : function( queue, isInstant )
40230     {
40231         var pos = this.el.getBox(true);
40232         var x = pos.x;
40233         var y = pos.y;
40234         var maxY = [];
40235         
40236         for (var i = 0; i < this.cols; i++){
40237             maxY[i] = pos.y;
40238         }
40239         
40240         Roo.each(queue, function(box, k){
40241             
40242             var col = k % this.cols;
40243             
40244             Roo.each(box, function(b,kk){
40245                 
40246                 b.el.position('absolute');
40247                 
40248                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
40249                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
40250                 
40251                 if(b.size == 'md-left' || b.size == 'md-right'){
40252                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
40253                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
40254                 }
40255                 
40256                 b.el.setWidth(width);
40257                 b.el.setHeight(height);
40258                 // iframe?
40259                 b.el.select('iframe',true).setSize(width,height);
40260                 
40261             }, this);
40262             
40263             for (var i = 0; i < this.cols; i++){
40264                 
40265                 if(maxY[i] < maxY[col]){
40266                     col = i;
40267                     continue;
40268                 }
40269                 
40270                 col = Math.min(col, i);
40271                 
40272             }
40273             
40274             x = pos.x + col * (this.colWidth + this.padWidth);
40275             
40276             y = maxY[col];
40277             
40278             var positions = [];
40279             
40280             switch (box.length){
40281                 case 1 :
40282                     positions = this.getVerticalOneBoxColPositions(x, y, box);
40283                     break;
40284                 case 2 :
40285                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
40286                     break;
40287                 case 3 :
40288                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
40289                     break;
40290                 case 4 :
40291                     positions = this.getVerticalFourBoxColPositions(x, y, box);
40292                     break;
40293                 default :
40294                     break;
40295             }
40296             
40297             Roo.each(box, function(b,kk){
40298                 
40299                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
40300                 
40301                 var sz = b.el.getSize();
40302                 
40303                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
40304                 
40305             }, this);
40306             
40307         }, this);
40308         
40309         var mY = 0;
40310         
40311         for (var i = 0; i < this.cols; i++){
40312             mY = Math.max(mY, maxY[i]);
40313         }
40314         
40315         this.el.setHeight(mY - pos.y);
40316         
40317     },
40318     
40319 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
40320 //    {
40321 //        var pos = this.el.getBox(true);
40322 //        var x = pos.x;
40323 //        var y = pos.y;
40324 //        var maxX = pos.right;
40325 //        
40326 //        var maxHeight = 0;
40327 //        
40328 //        Roo.each(items, function(item, k){
40329 //            
40330 //            var c = k % 2;
40331 //            
40332 //            item.el.position('absolute');
40333 //                
40334 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
40335 //
40336 //            item.el.setWidth(width);
40337 //
40338 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
40339 //
40340 //            item.el.setHeight(height);
40341 //            
40342 //            if(c == 0){
40343 //                item.el.setXY([x, y], isInstant ? false : true);
40344 //            } else {
40345 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
40346 //            }
40347 //            
40348 //            y = y + height + this.alternativePadWidth;
40349 //            
40350 //            maxHeight = maxHeight + height + this.alternativePadWidth;
40351 //            
40352 //        }, this);
40353 //        
40354 //        this.el.setHeight(maxHeight);
40355 //        
40356 //    },
40357     
40358     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
40359     {
40360         var pos = this.el.getBox(true);
40361         
40362         var minX = pos.x;
40363         var minY = pos.y;
40364         
40365         var maxX = pos.right;
40366         
40367         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
40368         
40369         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
40370         
40371         Roo.each(queue, function(box, k){
40372             
40373             Roo.each(box, function(b, kk){
40374                 
40375                 b.el.position('absolute');
40376                 
40377                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
40378                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
40379                 
40380                 if(b.size == 'md-left' || b.size == 'md-right'){
40381                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
40382                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
40383                 }
40384                 
40385                 b.el.setWidth(width);
40386                 b.el.setHeight(height);
40387                 
40388             }, this);
40389             
40390             if(!box.length){
40391                 return;
40392             }
40393             
40394             var positions = [];
40395             
40396             switch (box.length){
40397                 case 1 :
40398                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
40399                     break;
40400                 case 2 :
40401                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
40402                     break;
40403                 case 3 :
40404                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
40405                     break;
40406                 case 4 :
40407                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
40408                     break;
40409                 default :
40410                     break;
40411             }
40412             
40413             Roo.each(box, function(b,kk){
40414                 
40415                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
40416                 
40417                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
40418                 
40419             }, this);
40420             
40421         }, this);
40422         
40423     },
40424     
40425     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
40426     {
40427         Roo.each(eItems, function(b,k){
40428             
40429             b.size = (k == 0) ? 'sm' : 'xs';
40430             b.x = (k == 0) ? 2 : 1;
40431             b.y = (k == 0) ? 2 : 1;
40432             
40433             b.el.position('absolute');
40434             
40435             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
40436                 
40437             b.el.setWidth(width);
40438             
40439             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
40440             
40441             b.el.setHeight(height);
40442             
40443         }, this);
40444
40445         var positions = [];
40446         
40447         positions.push({
40448             x : maxX - this.unitWidth * 2 - this.gutter,
40449             y : minY
40450         });
40451         
40452         positions.push({
40453             x : maxX - this.unitWidth,
40454             y : minY + (this.unitWidth + this.gutter) * 2
40455         });
40456         
40457         positions.push({
40458             x : maxX - this.unitWidth * 3 - this.gutter * 2,
40459             y : minY
40460         });
40461         
40462         Roo.each(eItems, function(b,k){
40463             
40464             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
40465
40466         }, this);
40467         
40468     },
40469     
40470     getVerticalOneBoxColPositions : function(x, y, box)
40471     {
40472         var pos = [];
40473         
40474         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
40475         
40476         if(box[0].size == 'md-left'){
40477             rand = 0;
40478         }
40479         
40480         if(box[0].size == 'md-right'){
40481             rand = 1;
40482         }
40483         
40484         pos.push({
40485             x : x + (this.unitWidth + this.gutter) * rand,
40486             y : y
40487         });
40488         
40489         return pos;
40490     },
40491     
40492     getVerticalTwoBoxColPositions : function(x, y, box)
40493     {
40494         var pos = [];
40495         
40496         if(box[0].size == 'xs'){
40497             
40498             pos.push({
40499                 x : x,
40500                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
40501             });
40502
40503             pos.push({
40504                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
40505                 y : y
40506             });
40507             
40508             return pos;
40509             
40510         }
40511         
40512         pos.push({
40513             x : x,
40514             y : y
40515         });
40516
40517         pos.push({
40518             x : x + (this.unitWidth + this.gutter) * 2,
40519             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
40520         });
40521         
40522         return pos;
40523         
40524     },
40525     
40526     getVerticalThreeBoxColPositions : function(x, y, box)
40527     {
40528         var pos = [];
40529         
40530         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40531             
40532             pos.push({
40533                 x : x,
40534                 y : y
40535             });
40536
40537             pos.push({
40538                 x : x + (this.unitWidth + this.gutter) * 1,
40539                 y : y
40540             });
40541             
40542             pos.push({
40543                 x : x + (this.unitWidth + this.gutter) * 2,
40544                 y : y
40545             });
40546             
40547             return pos;
40548             
40549         }
40550         
40551         if(box[0].size == 'xs' && box[1].size == 'xs'){
40552             
40553             pos.push({
40554                 x : x,
40555                 y : y
40556             });
40557
40558             pos.push({
40559                 x : x,
40560                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
40561             });
40562             
40563             pos.push({
40564                 x : x + (this.unitWidth + this.gutter) * 1,
40565                 y : y
40566             });
40567             
40568             return pos;
40569             
40570         }
40571         
40572         pos.push({
40573             x : x,
40574             y : y
40575         });
40576
40577         pos.push({
40578             x : x + (this.unitWidth + this.gutter) * 2,
40579             y : y
40580         });
40581
40582         pos.push({
40583             x : x + (this.unitWidth + this.gutter) * 2,
40584             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40585         });
40586             
40587         return pos;
40588         
40589     },
40590     
40591     getVerticalFourBoxColPositions : function(x, y, box)
40592     {
40593         var pos = [];
40594         
40595         if(box[0].size == 'xs'){
40596             
40597             pos.push({
40598                 x : x,
40599                 y : y
40600             });
40601
40602             pos.push({
40603                 x : x,
40604                 y : y + (this.unitHeight + this.gutter) * 1
40605             });
40606             
40607             pos.push({
40608                 x : x,
40609                 y : y + (this.unitHeight + this.gutter) * 2
40610             });
40611             
40612             pos.push({
40613                 x : x + (this.unitWidth + this.gutter) * 1,
40614                 y : y
40615             });
40616             
40617             return pos;
40618             
40619         }
40620         
40621         pos.push({
40622             x : x,
40623             y : y
40624         });
40625
40626         pos.push({
40627             x : x + (this.unitWidth + this.gutter) * 2,
40628             y : y
40629         });
40630
40631         pos.push({
40632             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40633             y : y + (this.unitHeight + this.gutter) * 1
40634         });
40635
40636         pos.push({
40637             x : x + (this.unitWidth + this.gutter) * 2,
40638             y : y + (this.unitWidth + this.gutter) * 2
40639         });
40640
40641         return pos;
40642         
40643     },
40644     
40645     getHorizontalOneBoxColPositions : function(maxX, minY, box)
40646     {
40647         var pos = [];
40648         
40649         if(box[0].size == 'md-left'){
40650             pos.push({
40651                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40652                 y : minY
40653             });
40654             
40655             return pos;
40656         }
40657         
40658         if(box[0].size == 'md-right'){
40659             pos.push({
40660                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40661                 y : minY + (this.unitWidth + this.gutter) * 1
40662             });
40663             
40664             return pos;
40665         }
40666         
40667         var rand = Math.floor(Math.random() * (4 - box[0].y));
40668         
40669         pos.push({
40670             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40671             y : minY + (this.unitWidth + this.gutter) * rand
40672         });
40673         
40674         return pos;
40675         
40676     },
40677     
40678     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40679     {
40680         var pos = [];
40681         
40682         if(box[0].size == 'xs'){
40683             
40684             pos.push({
40685                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40686                 y : minY
40687             });
40688
40689             pos.push({
40690                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40691                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40692             });
40693             
40694             return pos;
40695             
40696         }
40697         
40698         pos.push({
40699             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40700             y : minY
40701         });
40702
40703         pos.push({
40704             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40705             y : minY + (this.unitWidth + this.gutter) * 2
40706         });
40707         
40708         return pos;
40709         
40710     },
40711     
40712     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40713     {
40714         var pos = [];
40715         
40716         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40717             
40718             pos.push({
40719                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40720                 y : minY
40721             });
40722
40723             pos.push({
40724                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40725                 y : minY + (this.unitWidth + this.gutter) * 1
40726             });
40727             
40728             pos.push({
40729                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40730                 y : minY + (this.unitWidth + this.gutter) * 2
40731             });
40732             
40733             return pos;
40734             
40735         }
40736         
40737         if(box[0].size == 'xs' && box[1].size == 'xs'){
40738             
40739             pos.push({
40740                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40741                 y : minY
40742             });
40743
40744             pos.push({
40745                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40746                 y : minY
40747             });
40748             
40749             pos.push({
40750                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40751                 y : minY + (this.unitWidth + this.gutter) * 1
40752             });
40753             
40754             return pos;
40755             
40756         }
40757         
40758         pos.push({
40759             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40760             y : minY
40761         });
40762
40763         pos.push({
40764             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40765             y : minY + (this.unitWidth + this.gutter) * 2
40766         });
40767
40768         pos.push({
40769             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40770             y : minY + (this.unitWidth + this.gutter) * 2
40771         });
40772             
40773         return pos;
40774         
40775     },
40776     
40777     getHorizontalFourBoxColPositions : function(maxX, minY, box)
40778     {
40779         var pos = [];
40780         
40781         if(box[0].size == 'xs'){
40782             
40783             pos.push({
40784                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40785                 y : minY
40786             });
40787
40788             pos.push({
40789                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40790                 y : minY
40791             });
40792             
40793             pos.push({
40794                 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),
40795                 y : minY
40796             });
40797             
40798             pos.push({
40799                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40800                 y : minY + (this.unitWidth + this.gutter) * 1
40801             });
40802             
40803             return pos;
40804             
40805         }
40806         
40807         pos.push({
40808             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40809             y : minY
40810         });
40811         
40812         pos.push({
40813             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40814             y : minY + (this.unitWidth + this.gutter) * 2
40815         });
40816         
40817         pos.push({
40818             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40819             y : minY + (this.unitWidth + this.gutter) * 2
40820         });
40821         
40822         pos.push({
40823             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),
40824             y : minY + (this.unitWidth + this.gutter) * 2
40825         });
40826
40827         return pos;
40828         
40829     },
40830     
40831     /**
40832     * remove a Masonry Brick
40833     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40834     */
40835     removeBrick : function(brick_id)
40836     {
40837         if (!brick_id) {
40838             return;
40839         }
40840         
40841         for (var i = 0; i<this.bricks.length; i++) {
40842             if (this.bricks[i].id == brick_id) {
40843                 this.bricks.splice(i,1);
40844                 this.el.dom.removeChild(Roo.get(brick_id).dom);
40845                 this.initial();
40846             }
40847         }
40848     },
40849     
40850     /**
40851     * adds a Masonry Brick
40852     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40853     */
40854     addBrick : function(cfg)
40855     {
40856         var cn = new Roo.bootstrap.MasonryBrick(cfg);
40857         //this.register(cn);
40858         cn.parentId = this.id;
40859         cn.render(this.el);
40860         return cn;
40861     },
40862     
40863     /**
40864     * register a Masonry Brick
40865     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40866     */
40867     
40868     register : function(brick)
40869     {
40870         this.bricks.push(brick);
40871         brick.masonryId = this.id;
40872     },
40873     
40874     /**
40875     * clear all the Masonry Brick
40876     */
40877     clearAll : function()
40878     {
40879         this.bricks = [];
40880         //this.getChildContainer().dom.innerHTML = "";
40881         this.el.dom.innerHTML = '';
40882     },
40883     
40884     getSelected : function()
40885     {
40886         if (!this.selectedBrick) {
40887             return false;
40888         }
40889         
40890         return this.selectedBrick;
40891     }
40892 });
40893
40894 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40895     
40896     groups: {},
40897      /**
40898     * register a Masonry Layout
40899     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40900     */
40901     
40902     register : function(layout)
40903     {
40904         this.groups[layout.id] = layout;
40905     },
40906     /**
40907     * fetch a  Masonry Layout based on the masonry layout ID
40908     * @param {string} the masonry layout to add
40909     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40910     */
40911     
40912     get: function(layout_id) {
40913         if (typeof(this.groups[layout_id]) == 'undefined') {
40914             return false;
40915         }
40916         return this.groups[layout_id] ;
40917     }
40918     
40919     
40920     
40921 });
40922
40923  
40924
40925  /**
40926  *
40927  * This is based on 
40928  * http://masonry.desandro.com
40929  *
40930  * The idea is to render all the bricks based on vertical width...
40931  *
40932  * The original code extends 'outlayer' - we might need to use that....
40933  * 
40934  */
40935
40936
40937 /**
40938  * @class Roo.bootstrap.LayoutMasonryAuto
40939  * @extends Roo.bootstrap.Component
40940  * Bootstrap Layout Masonry class
40941  * 
40942  * @constructor
40943  * Create a new Element
40944  * @param {Object} config The config object
40945  */
40946
40947 Roo.bootstrap.LayoutMasonryAuto = function(config){
40948     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40949 };
40950
40951 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40952     
40953       /**
40954      * @cfg {Boolean} isFitWidth  - resize the width..
40955      */   
40956     isFitWidth : false,  // options..
40957     /**
40958      * @cfg {Boolean} isOriginLeft = left align?
40959      */   
40960     isOriginLeft : true,
40961     /**
40962      * @cfg {Boolean} isOriginTop = top align?
40963      */   
40964     isOriginTop : false,
40965     /**
40966      * @cfg {Boolean} isLayoutInstant = no animation?
40967      */   
40968     isLayoutInstant : false, // needed?
40969     /**
40970      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40971      */   
40972     isResizingContainer : true,
40973     /**
40974      * @cfg {Number} columnWidth  width of the columns 
40975      */   
40976     
40977     columnWidth : 0,
40978     
40979     /**
40980      * @cfg {Number} maxCols maximum number of columns
40981      */   
40982     
40983     maxCols: 0,
40984     /**
40985      * @cfg {Number} padHeight padding below box..
40986      */   
40987     
40988     padHeight : 10, 
40989     
40990     /**
40991      * @cfg {Boolean} isAutoInitial defalut true
40992      */   
40993     
40994     isAutoInitial : true, 
40995     
40996     // private?
40997     gutter : 0,
40998     
40999     containerWidth: 0,
41000     initialColumnWidth : 0,
41001     currentSize : null,
41002     
41003     colYs : null, // array.
41004     maxY : 0,
41005     padWidth: 10,
41006     
41007     
41008     tag: 'div',
41009     cls: '',
41010     bricks: null, //CompositeElement
41011     cols : 0, // array?
41012     // element : null, // wrapped now this.el
41013     _isLayoutInited : null, 
41014     
41015     
41016     getAutoCreate : function(){
41017         
41018         var cfg = {
41019             tag: this.tag,
41020             cls: 'blog-masonary-wrapper ' + this.cls,
41021             cn : {
41022                 cls : 'mas-boxes masonary'
41023             }
41024         };
41025         
41026         return cfg;
41027     },
41028     
41029     getChildContainer: function( )
41030     {
41031         if (this.boxesEl) {
41032             return this.boxesEl;
41033         }
41034         
41035         this.boxesEl = this.el.select('.mas-boxes').first();
41036         
41037         return this.boxesEl;
41038     },
41039     
41040     
41041     initEvents : function()
41042     {
41043         var _this = this;
41044         
41045         if(this.isAutoInitial){
41046             Roo.log('hook children rendered');
41047             this.on('childrenrendered', function() {
41048                 Roo.log('children rendered');
41049                 _this.initial();
41050             } ,this);
41051         }
41052         
41053     },
41054     
41055     initial : function()
41056     {
41057         this.reloadItems();
41058
41059         this.currentSize = this.el.getBox(true);
41060
41061         /// was window resize... - let's see if this works..
41062         Roo.EventManager.onWindowResize(this.resize, this); 
41063
41064         if(!this.isAutoInitial){
41065             this.layout();
41066             return;
41067         }
41068         
41069         this.layout.defer(500,this);
41070     },
41071     
41072     reloadItems: function()
41073     {
41074         this.bricks = this.el.select('.masonry-brick', true);
41075         
41076         this.bricks.each(function(b) {
41077             //Roo.log(b.getSize());
41078             if (!b.attr('originalwidth')) {
41079                 b.attr('originalwidth',  b.getSize().width);
41080             }
41081             
41082         });
41083         
41084         Roo.log(this.bricks.elements.length);
41085     },
41086     
41087     resize : function()
41088     {
41089         Roo.log('resize');
41090         var cs = this.el.getBox(true);
41091         
41092         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
41093             Roo.log("no change in with or X");
41094             return;
41095         }
41096         this.currentSize = cs;
41097         this.layout();
41098     },
41099     
41100     layout : function()
41101     {
41102          Roo.log('layout');
41103         this._resetLayout();
41104         //this._manageStamps();
41105       
41106         // don't animate first layout
41107         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
41108         this.layoutItems( isInstant );
41109       
41110         // flag for initalized
41111         this._isLayoutInited = true;
41112     },
41113     
41114     layoutItems : function( isInstant )
41115     {
41116         //var items = this._getItemsForLayout( this.items );
41117         // original code supports filtering layout items.. we just ignore it..
41118         
41119         this._layoutItems( this.bricks , isInstant );
41120       
41121         this._postLayout();
41122     },
41123     _layoutItems : function ( items , isInstant)
41124     {
41125        //this.fireEvent( 'layout', this, items );
41126     
41127
41128         if ( !items || !items.elements.length ) {
41129           // no items, emit event with empty array
41130             return;
41131         }
41132
41133         var queue = [];
41134         items.each(function(item) {
41135             Roo.log("layout item");
41136             Roo.log(item);
41137             // get x/y object from method
41138             var position = this._getItemLayoutPosition( item );
41139             // enqueue
41140             position.item = item;
41141             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
41142             queue.push( position );
41143         }, this);
41144       
41145         this._processLayoutQueue( queue );
41146     },
41147     /** Sets position of item in DOM
41148     * @param {Element} item
41149     * @param {Number} x - horizontal position
41150     * @param {Number} y - vertical position
41151     * @param {Boolean} isInstant - disables transitions
41152     */
41153     _processLayoutQueue : function( queue )
41154     {
41155         for ( var i=0, len = queue.length; i < len; i++ ) {
41156             var obj = queue[i];
41157             obj.item.position('absolute');
41158             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
41159         }
41160     },
41161       
41162     
41163     /**
41164     * Any logic you want to do after each layout,
41165     * i.e. size the container
41166     */
41167     _postLayout : function()
41168     {
41169         this.resizeContainer();
41170     },
41171     
41172     resizeContainer : function()
41173     {
41174         if ( !this.isResizingContainer ) {
41175             return;
41176         }
41177         var size = this._getContainerSize();
41178         if ( size ) {
41179             this.el.setSize(size.width,size.height);
41180             this.boxesEl.setSize(size.width,size.height);
41181         }
41182     },
41183     
41184     
41185     
41186     _resetLayout : function()
41187     {
41188         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
41189         this.colWidth = this.el.getWidth();
41190         //this.gutter = this.el.getWidth(); 
41191         
41192         this.measureColumns();
41193
41194         // reset column Y
41195         var i = this.cols;
41196         this.colYs = [];
41197         while (i--) {
41198             this.colYs.push( 0 );
41199         }
41200     
41201         this.maxY = 0;
41202     },
41203
41204     measureColumns : function()
41205     {
41206         this.getContainerWidth();
41207       // if columnWidth is 0, default to outerWidth of first item
41208         if ( !this.columnWidth ) {
41209             var firstItem = this.bricks.first();
41210             Roo.log(firstItem);
41211             this.columnWidth  = this.containerWidth;
41212             if (firstItem && firstItem.attr('originalwidth') ) {
41213                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
41214             }
41215             // columnWidth fall back to item of first element
41216             Roo.log("set column width?");
41217                         this.initialColumnWidth = this.columnWidth  ;
41218
41219             // if first elem has no width, default to size of container
41220             
41221         }
41222         
41223         
41224         if (this.initialColumnWidth) {
41225             this.columnWidth = this.initialColumnWidth;
41226         }
41227         
41228         
41229             
41230         // column width is fixed at the top - however if container width get's smaller we should
41231         // reduce it...
41232         
41233         // this bit calcs how man columns..
41234             
41235         var columnWidth = this.columnWidth += this.gutter;
41236       
41237         // calculate columns
41238         var containerWidth = this.containerWidth + this.gutter;
41239         
41240         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
41241         // fix rounding errors, typically with gutters
41242         var excess = columnWidth - containerWidth % columnWidth;
41243         
41244         
41245         // if overshoot is less than a pixel, round up, otherwise floor it
41246         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
41247         cols = Math[ mathMethod ]( cols );
41248         this.cols = Math.max( cols, 1 );
41249         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
41250         
41251          // padding positioning..
41252         var totalColWidth = this.cols * this.columnWidth;
41253         var padavail = this.containerWidth - totalColWidth;
41254         // so for 2 columns - we need 3 'pads'
41255         
41256         var padNeeded = (1+this.cols) * this.padWidth;
41257         
41258         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
41259         
41260         this.columnWidth += padExtra
41261         //this.padWidth = Math.floor(padavail /  ( this.cols));
41262         
41263         // adjust colum width so that padding is fixed??
41264         
41265         // we have 3 columns ... total = width * 3
41266         // we have X left over... that should be used by 
41267         
41268         //if (this.expandC) {
41269             
41270         //}
41271         
41272         
41273         
41274     },
41275     
41276     getContainerWidth : function()
41277     {
41278        /* // container is parent if fit width
41279         var container = this.isFitWidth ? this.element.parentNode : this.element;
41280         // check that this.size and size are there
41281         // IE8 triggers resize on body size change, so they might not be
41282         
41283         var size = getSize( container );  //FIXME
41284         this.containerWidth = size && size.innerWidth; //FIXME
41285         */
41286          
41287         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
41288         
41289     },
41290     
41291     _getItemLayoutPosition : function( item )  // what is item?
41292     {
41293         // we resize the item to our columnWidth..
41294       
41295         item.setWidth(this.columnWidth);
41296         item.autoBoxAdjust  = false;
41297         
41298         var sz = item.getSize();
41299  
41300         // how many columns does this brick span
41301         var remainder = this.containerWidth % this.columnWidth;
41302         
41303         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
41304         // round if off by 1 pixel, otherwise use ceil
41305         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
41306         colSpan = Math.min( colSpan, this.cols );
41307         
41308         // normally this should be '1' as we dont' currently allow multi width columns..
41309         
41310         var colGroup = this._getColGroup( colSpan );
41311         // get the minimum Y value from the columns
41312         var minimumY = Math.min.apply( Math, colGroup );
41313         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
41314         
41315         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
41316          
41317         // position the brick
41318         var position = {
41319             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
41320             y: this.currentSize.y + minimumY + this.padHeight
41321         };
41322         
41323         Roo.log(position);
41324         // apply setHeight to necessary columns
41325         var setHeight = minimumY + sz.height + this.padHeight;
41326         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
41327         
41328         var setSpan = this.cols + 1 - colGroup.length;
41329         for ( var i = 0; i < setSpan; i++ ) {
41330           this.colYs[ shortColIndex + i ] = setHeight ;
41331         }
41332       
41333         return position;
41334     },
41335     
41336     /**
41337      * @param {Number} colSpan - number of columns the element spans
41338      * @returns {Array} colGroup
41339      */
41340     _getColGroup : function( colSpan )
41341     {
41342         if ( colSpan < 2 ) {
41343           // if brick spans only one column, use all the column Ys
41344           return this.colYs;
41345         }
41346       
41347         var colGroup = [];
41348         // how many different places could this brick fit horizontally
41349         var groupCount = this.cols + 1 - colSpan;
41350         // for each group potential horizontal position
41351         for ( var i = 0; i < groupCount; i++ ) {
41352           // make an array of colY values for that one group
41353           var groupColYs = this.colYs.slice( i, i + colSpan );
41354           // and get the max value of the array
41355           colGroup[i] = Math.max.apply( Math, groupColYs );
41356         }
41357         return colGroup;
41358     },
41359     /*
41360     _manageStamp : function( stamp )
41361     {
41362         var stampSize =  stamp.getSize();
41363         var offset = stamp.getBox();
41364         // get the columns that this stamp affects
41365         var firstX = this.isOriginLeft ? offset.x : offset.right;
41366         var lastX = firstX + stampSize.width;
41367         var firstCol = Math.floor( firstX / this.columnWidth );
41368         firstCol = Math.max( 0, firstCol );
41369         
41370         var lastCol = Math.floor( lastX / this.columnWidth );
41371         // lastCol should not go over if multiple of columnWidth #425
41372         lastCol -= lastX % this.columnWidth ? 0 : 1;
41373         lastCol = Math.min( this.cols - 1, lastCol );
41374         
41375         // set colYs to bottom of the stamp
41376         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
41377             stampSize.height;
41378             
41379         for ( var i = firstCol; i <= lastCol; i++ ) {
41380           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
41381         }
41382     },
41383     */
41384     
41385     _getContainerSize : function()
41386     {
41387         this.maxY = Math.max.apply( Math, this.colYs );
41388         var size = {
41389             height: this.maxY
41390         };
41391       
41392         if ( this.isFitWidth ) {
41393             size.width = this._getContainerFitWidth();
41394         }
41395       
41396         return size;
41397     },
41398     
41399     _getContainerFitWidth : function()
41400     {
41401         var unusedCols = 0;
41402         // count unused columns
41403         var i = this.cols;
41404         while ( --i ) {
41405           if ( this.colYs[i] !== 0 ) {
41406             break;
41407           }
41408           unusedCols++;
41409         }
41410         // fit container to columns that have been used
41411         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
41412     },
41413     
41414     needsResizeLayout : function()
41415     {
41416         var previousWidth = this.containerWidth;
41417         this.getContainerWidth();
41418         return previousWidth !== this.containerWidth;
41419     }
41420  
41421 });
41422
41423  
41424
41425  /*
41426  * - LGPL
41427  *
41428  * element
41429  * 
41430  */
41431
41432 /**
41433  * @class Roo.bootstrap.MasonryBrick
41434  * @extends Roo.bootstrap.Component
41435  * Bootstrap MasonryBrick class
41436  * 
41437  * @constructor
41438  * Create a new MasonryBrick
41439  * @param {Object} config The config object
41440  */
41441
41442 Roo.bootstrap.MasonryBrick = function(config){
41443     
41444     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
41445     
41446     Roo.bootstrap.MasonryBrick.register(this);
41447     
41448     this.addEvents({
41449         // raw events
41450         /**
41451          * @event click
41452          * When a MasonryBrick is clcik
41453          * @param {Roo.bootstrap.MasonryBrick} this
41454          * @param {Roo.EventObject} e
41455          */
41456         "click" : true
41457     });
41458 };
41459
41460 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
41461     
41462     /**
41463      * @cfg {String} title
41464      */   
41465     title : '',
41466     /**
41467      * @cfg {String} html
41468      */   
41469     html : '',
41470     /**
41471      * @cfg {String} bgimage
41472      */   
41473     bgimage : '',
41474     /**
41475      * @cfg {String} videourl
41476      */   
41477     videourl : '',
41478     /**
41479      * @cfg {String} cls
41480      */   
41481     cls : '',
41482     /**
41483      * @cfg {String} href
41484      */   
41485     href : '',
41486     /**
41487      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
41488      */   
41489     size : 'xs',
41490     
41491     /**
41492      * @cfg {String} placetitle (center|bottom)
41493      */   
41494     placetitle : '',
41495     
41496     /**
41497      * @cfg {Boolean} isFitContainer defalut true
41498      */   
41499     isFitContainer : true, 
41500     
41501     /**
41502      * @cfg {Boolean} preventDefault defalut false
41503      */   
41504     preventDefault : false, 
41505     
41506     /**
41507      * @cfg {Boolean} inverse defalut false
41508      */   
41509     maskInverse : false, 
41510     
41511     getAutoCreate : function()
41512     {
41513         if(!this.isFitContainer){
41514             return this.getSplitAutoCreate();
41515         }
41516         
41517         var cls = 'masonry-brick masonry-brick-full';
41518         
41519         if(this.href.length){
41520             cls += ' masonry-brick-link';
41521         }
41522         
41523         if(this.bgimage.length){
41524             cls += ' masonry-brick-image';
41525         }
41526         
41527         if(this.maskInverse){
41528             cls += ' mask-inverse';
41529         }
41530         
41531         if(!this.html.length && !this.maskInverse && !this.videourl.length){
41532             cls += ' enable-mask';
41533         }
41534         
41535         if(this.size){
41536             cls += ' masonry-' + this.size + '-brick';
41537         }
41538         
41539         if(this.placetitle.length){
41540             
41541             switch (this.placetitle) {
41542                 case 'center' :
41543                     cls += ' masonry-center-title';
41544                     break;
41545                 case 'bottom' :
41546                     cls += ' masonry-bottom-title';
41547                     break;
41548                 default:
41549                     break;
41550             }
41551             
41552         } else {
41553             if(!this.html.length && !this.bgimage.length){
41554                 cls += ' masonry-center-title';
41555             }
41556
41557             if(!this.html.length && this.bgimage.length){
41558                 cls += ' masonry-bottom-title';
41559             }
41560         }
41561         
41562         if(this.cls){
41563             cls += ' ' + this.cls;
41564         }
41565         
41566         var cfg = {
41567             tag: (this.href.length) ? 'a' : 'div',
41568             cls: cls,
41569             cn: [
41570                 {
41571                     tag: 'div',
41572                     cls: 'masonry-brick-mask'
41573                 },
41574                 {
41575                     tag: 'div',
41576                     cls: 'masonry-brick-paragraph',
41577                     cn: []
41578                 }
41579             ]
41580         };
41581         
41582         if(this.href.length){
41583             cfg.href = this.href;
41584         }
41585         
41586         var cn = cfg.cn[1].cn;
41587         
41588         if(this.title.length){
41589             cn.push({
41590                 tag: 'h4',
41591                 cls: 'masonry-brick-title',
41592                 html: this.title
41593             });
41594         }
41595         
41596         if(this.html.length){
41597             cn.push({
41598                 tag: 'p',
41599                 cls: 'masonry-brick-text',
41600                 html: this.html
41601             });
41602         }
41603         
41604         if (!this.title.length && !this.html.length) {
41605             cfg.cn[1].cls += ' hide';
41606         }
41607         
41608         if(this.bgimage.length){
41609             cfg.cn.push({
41610                 tag: 'img',
41611                 cls: 'masonry-brick-image-view',
41612                 src: this.bgimage
41613             });
41614         }
41615         
41616         if(this.videourl.length){
41617             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41618             // youtube support only?
41619             cfg.cn.push({
41620                 tag: 'iframe',
41621                 cls: 'masonry-brick-image-view',
41622                 src: vurl,
41623                 frameborder : 0,
41624                 allowfullscreen : true
41625             });
41626         }
41627         
41628         return cfg;
41629         
41630     },
41631     
41632     getSplitAutoCreate : function()
41633     {
41634         var cls = 'masonry-brick masonry-brick-split';
41635         
41636         if(this.href.length){
41637             cls += ' masonry-brick-link';
41638         }
41639         
41640         if(this.bgimage.length){
41641             cls += ' masonry-brick-image';
41642         }
41643         
41644         if(this.size){
41645             cls += ' masonry-' + this.size + '-brick';
41646         }
41647         
41648         switch (this.placetitle) {
41649             case 'center' :
41650                 cls += ' masonry-center-title';
41651                 break;
41652             case 'bottom' :
41653                 cls += ' masonry-bottom-title';
41654                 break;
41655             default:
41656                 if(!this.bgimage.length){
41657                     cls += ' masonry-center-title';
41658                 }
41659
41660                 if(this.bgimage.length){
41661                     cls += ' masonry-bottom-title';
41662                 }
41663                 break;
41664         }
41665         
41666         if(this.cls){
41667             cls += ' ' + this.cls;
41668         }
41669         
41670         var cfg = {
41671             tag: (this.href.length) ? 'a' : 'div',
41672             cls: cls,
41673             cn: [
41674                 {
41675                     tag: 'div',
41676                     cls: 'masonry-brick-split-head',
41677                     cn: [
41678                         {
41679                             tag: 'div',
41680                             cls: 'masonry-brick-paragraph',
41681                             cn: []
41682                         }
41683                     ]
41684                 },
41685                 {
41686                     tag: 'div',
41687                     cls: 'masonry-brick-split-body',
41688                     cn: []
41689                 }
41690             ]
41691         };
41692         
41693         if(this.href.length){
41694             cfg.href = this.href;
41695         }
41696         
41697         if(this.title.length){
41698             cfg.cn[0].cn[0].cn.push({
41699                 tag: 'h4',
41700                 cls: 'masonry-brick-title',
41701                 html: this.title
41702             });
41703         }
41704         
41705         if(this.html.length){
41706             cfg.cn[1].cn.push({
41707                 tag: 'p',
41708                 cls: 'masonry-brick-text',
41709                 html: this.html
41710             });
41711         }
41712
41713         if(this.bgimage.length){
41714             cfg.cn[0].cn.push({
41715                 tag: 'img',
41716                 cls: 'masonry-brick-image-view',
41717                 src: this.bgimage
41718             });
41719         }
41720         
41721         if(this.videourl.length){
41722             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41723             // youtube support only?
41724             cfg.cn[0].cn.cn.push({
41725                 tag: 'iframe',
41726                 cls: 'masonry-brick-image-view',
41727                 src: vurl,
41728                 frameborder : 0,
41729                 allowfullscreen : true
41730             });
41731         }
41732         
41733         return cfg;
41734     },
41735     
41736     initEvents: function() 
41737     {
41738         switch (this.size) {
41739             case 'xs' :
41740                 this.x = 1;
41741                 this.y = 1;
41742                 break;
41743             case 'sm' :
41744                 this.x = 2;
41745                 this.y = 2;
41746                 break;
41747             case 'md' :
41748             case 'md-left' :
41749             case 'md-right' :
41750                 this.x = 3;
41751                 this.y = 3;
41752                 break;
41753             case 'tall' :
41754                 this.x = 2;
41755                 this.y = 3;
41756                 break;
41757             case 'wide' :
41758                 this.x = 3;
41759                 this.y = 2;
41760                 break;
41761             case 'wide-thin' :
41762                 this.x = 3;
41763                 this.y = 1;
41764                 break;
41765                         
41766             default :
41767                 break;
41768         }
41769         
41770         if(Roo.isTouch){
41771             this.el.on('touchstart', this.onTouchStart, this);
41772             this.el.on('touchmove', this.onTouchMove, this);
41773             this.el.on('touchend', this.onTouchEnd, this);
41774             this.el.on('contextmenu', this.onContextMenu, this);
41775         } else {
41776             this.el.on('mouseenter'  ,this.enter, this);
41777             this.el.on('mouseleave', this.leave, this);
41778             this.el.on('click', this.onClick, this);
41779         }
41780         
41781         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41782             this.parent().bricks.push(this);   
41783         }
41784         
41785     },
41786     
41787     onClick: function(e, el)
41788     {
41789         var time = this.endTimer - this.startTimer;
41790         // Roo.log(e.preventDefault());
41791         if(Roo.isTouch){
41792             if(time > 1000){
41793                 e.preventDefault();
41794                 return;
41795             }
41796         }
41797         
41798         if(!this.preventDefault){
41799             return;
41800         }
41801         
41802         e.preventDefault();
41803         
41804         if (this.activeClass != '') {
41805             this.selectBrick();
41806         }
41807         
41808         this.fireEvent('click', this, e);
41809     },
41810     
41811     enter: function(e, el)
41812     {
41813         e.preventDefault();
41814         
41815         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41816             return;
41817         }
41818         
41819         if(this.bgimage.length && this.html.length){
41820             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41821         }
41822     },
41823     
41824     leave: function(e, el)
41825     {
41826         e.preventDefault();
41827         
41828         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
41829             return;
41830         }
41831         
41832         if(this.bgimage.length && this.html.length){
41833             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41834         }
41835     },
41836     
41837     onTouchStart: function(e, el)
41838     {
41839 //        e.preventDefault();
41840         
41841         this.touchmoved = false;
41842         
41843         if(!this.isFitContainer){
41844             return;
41845         }
41846         
41847         if(!this.bgimage.length || !this.html.length){
41848             return;
41849         }
41850         
41851         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41852         
41853         this.timer = new Date().getTime();
41854         
41855     },
41856     
41857     onTouchMove: function(e, el)
41858     {
41859         this.touchmoved = true;
41860     },
41861     
41862     onContextMenu : function(e,el)
41863     {
41864         e.preventDefault();
41865         e.stopPropagation();
41866         return false;
41867     },
41868     
41869     onTouchEnd: function(e, el)
41870     {
41871 //        e.preventDefault();
41872         
41873         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41874         
41875             this.leave(e,el);
41876             
41877             return;
41878         }
41879         
41880         if(!this.bgimage.length || !this.html.length){
41881             
41882             if(this.href.length){
41883                 window.location.href = this.href;
41884             }
41885             
41886             return;
41887         }
41888         
41889         if(!this.isFitContainer){
41890             return;
41891         }
41892         
41893         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41894         
41895         window.location.href = this.href;
41896     },
41897     
41898     //selection on single brick only
41899     selectBrick : function() {
41900         
41901         if (!this.parentId) {
41902             return;
41903         }
41904         
41905         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41906         var index = m.selectedBrick.indexOf(this.id);
41907         
41908         if ( index > -1) {
41909             m.selectedBrick.splice(index,1);
41910             this.el.removeClass(this.activeClass);
41911             return;
41912         }
41913         
41914         for(var i = 0; i < m.selectedBrick.length; i++) {
41915             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41916             b.el.removeClass(b.activeClass);
41917         }
41918         
41919         m.selectedBrick = [];
41920         
41921         m.selectedBrick.push(this.id);
41922         this.el.addClass(this.activeClass);
41923         return;
41924     },
41925     
41926     isSelected : function(){
41927         return this.el.hasClass(this.activeClass);
41928         
41929     }
41930 });
41931
41932 Roo.apply(Roo.bootstrap.MasonryBrick, {
41933     
41934     //groups: {},
41935     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41936      /**
41937     * register a Masonry Brick
41938     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41939     */
41940     
41941     register : function(brick)
41942     {
41943         //this.groups[brick.id] = brick;
41944         this.groups.add(brick.id, brick);
41945     },
41946     /**
41947     * fetch a  masonry brick based on the masonry brick ID
41948     * @param {string} the masonry brick to add
41949     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41950     */
41951     
41952     get: function(brick_id) 
41953     {
41954         // if (typeof(this.groups[brick_id]) == 'undefined') {
41955         //     return false;
41956         // }
41957         // return this.groups[brick_id] ;
41958         
41959         if(this.groups.key(brick_id)) {
41960             return this.groups.key(brick_id);
41961         }
41962         
41963         return false;
41964     }
41965     
41966     
41967     
41968 });
41969
41970  /*
41971  * - LGPL
41972  *
41973  * element
41974  * 
41975  */
41976
41977 /**
41978  * @class Roo.bootstrap.Brick
41979  * @extends Roo.bootstrap.Component
41980  * Bootstrap Brick class
41981  * 
41982  * @constructor
41983  * Create a new Brick
41984  * @param {Object} config The config object
41985  */
41986
41987 Roo.bootstrap.Brick = function(config){
41988     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41989     
41990     this.addEvents({
41991         // raw events
41992         /**
41993          * @event click
41994          * When a Brick is click
41995          * @param {Roo.bootstrap.Brick} this
41996          * @param {Roo.EventObject} e
41997          */
41998         "click" : true
41999     });
42000 };
42001
42002 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
42003     
42004     /**
42005      * @cfg {String} title
42006      */   
42007     title : '',
42008     /**
42009      * @cfg {String} html
42010      */   
42011     html : '',
42012     /**
42013      * @cfg {String} bgimage
42014      */   
42015     bgimage : '',
42016     /**
42017      * @cfg {String} cls
42018      */   
42019     cls : '',
42020     /**
42021      * @cfg {String} href
42022      */   
42023     href : '',
42024     /**
42025      * @cfg {String} video
42026      */   
42027     video : '',
42028     /**
42029      * @cfg {Boolean} square
42030      */   
42031     square : true,
42032     
42033     getAutoCreate : function()
42034     {
42035         var cls = 'roo-brick';
42036         
42037         if(this.href.length){
42038             cls += ' roo-brick-link';
42039         }
42040         
42041         if(this.bgimage.length){
42042             cls += ' roo-brick-image';
42043         }
42044         
42045         if(!this.html.length && !this.bgimage.length){
42046             cls += ' roo-brick-center-title';
42047         }
42048         
42049         if(!this.html.length && this.bgimage.length){
42050             cls += ' roo-brick-bottom-title';
42051         }
42052         
42053         if(this.cls){
42054             cls += ' ' + this.cls;
42055         }
42056         
42057         var cfg = {
42058             tag: (this.href.length) ? 'a' : 'div',
42059             cls: cls,
42060             cn: [
42061                 {
42062                     tag: 'div',
42063                     cls: 'roo-brick-paragraph',
42064                     cn: []
42065                 }
42066             ]
42067         };
42068         
42069         if(this.href.length){
42070             cfg.href = this.href;
42071         }
42072         
42073         var cn = cfg.cn[0].cn;
42074         
42075         if(this.title.length){
42076             cn.push({
42077                 tag: 'h4',
42078                 cls: 'roo-brick-title',
42079                 html: this.title
42080             });
42081         }
42082         
42083         if(this.html.length){
42084             cn.push({
42085                 tag: 'p',
42086                 cls: 'roo-brick-text',
42087                 html: this.html
42088             });
42089         } else {
42090             cn.cls += ' hide';
42091         }
42092         
42093         if(this.bgimage.length){
42094             cfg.cn.push({
42095                 tag: 'img',
42096                 cls: 'roo-brick-image-view',
42097                 src: this.bgimage
42098             });
42099         }
42100         
42101         return cfg;
42102     },
42103     
42104     initEvents: function() 
42105     {
42106         if(this.title.length || this.html.length){
42107             this.el.on('mouseenter'  ,this.enter, this);
42108             this.el.on('mouseleave', this.leave, this);
42109         }
42110         
42111         Roo.EventManager.onWindowResize(this.resize, this); 
42112         
42113         if(this.bgimage.length){
42114             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
42115             this.imageEl.on('load', this.onImageLoad, this);
42116             return;
42117         }
42118         
42119         this.resize();
42120     },
42121     
42122     onImageLoad : function()
42123     {
42124         this.resize();
42125     },
42126     
42127     resize : function()
42128     {
42129         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
42130         
42131         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
42132         
42133         if(this.bgimage.length){
42134             var image = this.el.select('.roo-brick-image-view', true).first();
42135             
42136             image.setWidth(paragraph.getWidth());
42137             
42138             if(this.square){
42139                 image.setHeight(paragraph.getWidth());
42140             }
42141             
42142             this.el.setHeight(image.getHeight());
42143             paragraph.setHeight(image.getHeight());
42144             
42145         }
42146         
42147     },
42148     
42149     enter: function(e, el)
42150     {
42151         e.preventDefault();
42152         
42153         if(this.bgimage.length){
42154             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
42155             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
42156         }
42157     },
42158     
42159     leave: function(e, el)
42160     {
42161         e.preventDefault();
42162         
42163         if(this.bgimage.length){
42164             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
42165             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
42166         }
42167     }
42168     
42169 });
42170
42171  
42172
42173  /*
42174  * - LGPL
42175  *
42176  * Number field 
42177  */
42178
42179 /**
42180  * @class Roo.bootstrap.form.NumberField
42181  * @extends Roo.bootstrap.form.Input
42182  * Bootstrap NumberField class
42183  * 
42184  * 
42185  * 
42186  * 
42187  * @constructor
42188  * Create a new NumberField
42189  * @param {Object} config The config object
42190  */
42191
42192 Roo.bootstrap.form.NumberField = function(config){
42193     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
42194 };
42195
42196 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
42197     
42198     /**
42199      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42200      */
42201     allowDecimals : true,
42202     /**
42203      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42204      */
42205     decimalSeparator : ".",
42206     /**
42207      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42208      */
42209     decimalPrecision : 2,
42210     /**
42211      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42212      */
42213     allowNegative : true,
42214     
42215     /**
42216      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42217      */
42218     allowZero: true,
42219     /**
42220      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42221      */
42222     minValue : Number.NEGATIVE_INFINITY,
42223     /**
42224      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42225      */
42226     maxValue : Number.MAX_VALUE,
42227     /**
42228      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42229      */
42230     minText : "The minimum value for this field is {0}",
42231     /**
42232      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42233      */
42234     maxText : "The maximum value for this field is {0}",
42235     /**
42236      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42237      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42238      */
42239     nanText : "{0} is not a valid number",
42240     /**
42241      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42242      */
42243     thousandsDelimiter : false,
42244     /**
42245      * @cfg {String} valueAlign alignment of value
42246      */
42247     valueAlign : "left",
42248
42249     getAutoCreate : function()
42250     {
42251         var hiddenInput = {
42252             tag: 'input',
42253             type: 'hidden',
42254             id: Roo.id(),
42255             cls: 'hidden-number-input'
42256         };
42257         
42258         if (this.name) {
42259             hiddenInput.name = this.name;
42260         }
42261         
42262         this.name = '';
42263         
42264         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
42265         
42266         this.name = hiddenInput.name;
42267         
42268         if(cfg.cn.length > 0) {
42269             cfg.cn.push(hiddenInput);
42270         }
42271         
42272         return cfg;
42273     },
42274
42275     // private
42276     initEvents : function()
42277     {   
42278         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
42279         
42280         var allowed = "0123456789";
42281         
42282         if(this.allowDecimals){
42283             allowed += this.decimalSeparator;
42284         }
42285         
42286         if(this.allowNegative){
42287             allowed += "-";
42288         }
42289         
42290         if(this.thousandsDelimiter) {
42291             allowed += ",";
42292         }
42293         
42294         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42295         
42296         var keyPress = function(e){
42297             
42298             var k = e.getKey();
42299             
42300             var c = e.getCharCode();
42301             
42302             if(
42303                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42304                     allowed.indexOf(String.fromCharCode(c)) === -1
42305             ){
42306                 e.stopEvent();
42307                 return;
42308             }
42309             
42310             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42311                 return;
42312             }
42313             
42314             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42315                 e.stopEvent();
42316             }
42317         };
42318         
42319         this.el.on("keypress", keyPress, this);
42320     },
42321     
42322     validateValue : function(value)
42323     {
42324         
42325         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
42326             return false;
42327         }
42328         
42329         var num = this.parseValue(value);
42330         
42331         if(isNaN(num)){
42332             this.markInvalid(String.format(this.nanText, value));
42333             return false;
42334         }
42335         
42336         if(num < this.minValue){
42337             this.markInvalid(String.format(this.minText, this.minValue));
42338             return false;
42339         }
42340         
42341         if(num > this.maxValue){
42342             this.markInvalid(String.format(this.maxText, this.maxValue));
42343             return false;
42344         }
42345         
42346         return true;
42347     },
42348
42349     getValue : function()
42350     {
42351         var v = this.hiddenEl().getValue();
42352         
42353         return this.fixPrecision(this.parseValue(v));
42354     },
42355
42356     parseValue : function(value)
42357     {
42358         if(this.thousandsDelimiter) {
42359             value += "";
42360             r = new RegExp(",", "g");
42361             value = value.replace(r, "");
42362         }
42363         
42364         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42365         return isNaN(value) ? '' : value;
42366     },
42367
42368     fixPrecision : function(value)
42369     {
42370         if(this.thousandsDelimiter) {
42371             value += "";
42372             r = new RegExp(",", "g");
42373             value = value.replace(r, "");
42374         }
42375         
42376         var nan = isNaN(value);
42377         
42378         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42379             return nan ? '' : value;
42380         }
42381         return parseFloat(value).toFixed(this.decimalPrecision);
42382     },
42383
42384     setValue : function(v)
42385     {
42386         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42387         
42388         this.value = v;
42389         
42390         if(this.rendered){
42391             
42392             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42393             
42394             this.inputEl().dom.value = (v == '') ? '' :
42395                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42396             
42397             if(!this.allowZero && v === '0') {
42398                 this.hiddenEl().dom.value = '';
42399                 this.inputEl().dom.value = '';
42400             }
42401             
42402             this.validate();
42403         }
42404     },
42405
42406     decimalPrecisionFcn : function(v)
42407     {
42408         return Math.floor(v);
42409     },
42410
42411     beforeBlur : function()
42412     {
42413         var v = this.parseValue(this.getRawValue());
42414         
42415         if(v || v === 0 || v === ''){
42416             this.setValue(v);
42417         }
42418     },
42419     
42420     hiddenEl : function()
42421     {
42422         return this.el.select('input.hidden-number-input',true).first();
42423     }
42424     
42425 });
42426
42427  
42428
42429 /*
42430 * Licence: LGPL
42431 */
42432
42433 /**
42434  * @class Roo.bootstrap.DocumentSlider
42435  * @extends Roo.bootstrap.Component
42436  * Bootstrap DocumentSlider class
42437  * 
42438  * @constructor
42439  * Create a new DocumentViewer
42440  * @param {Object} config The config object
42441  */
42442
42443 Roo.bootstrap.DocumentSlider = function(config){
42444     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
42445     
42446     this.files = [];
42447     
42448     this.addEvents({
42449         /**
42450          * @event initial
42451          * Fire after initEvent
42452          * @param {Roo.bootstrap.DocumentSlider} this
42453          */
42454         "initial" : true,
42455         /**
42456          * @event update
42457          * Fire after update
42458          * @param {Roo.bootstrap.DocumentSlider} this
42459          */
42460         "update" : true,
42461         /**
42462          * @event click
42463          * Fire after click
42464          * @param {Roo.bootstrap.DocumentSlider} this
42465          */
42466         "click" : true
42467     });
42468 };
42469
42470 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
42471     
42472     files : false,
42473     
42474     indicator : 0,
42475     
42476     getAutoCreate : function()
42477     {
42478         var cfg = {
42479             tag : 'div',
42480             cls : 'roo-document-slider',
42481             cn : [
42482                 {
42483                     tag : 'div',
42484                     cls : 'roo-document-slider-header',
42485                     cn : [
42486                         {
42487                             tag : 'div',
42488                             cls : 'roo-document-slider-header-title'
42489                         }
42490                     ]
42491                 },
42492                 {
42493                     tag : 'div',
42494                     cls : 'roo-document-slider-body',
42495                     cn : [
42496                         {
42497                             tag : 'div',
42498                             cls : 'roo-document-slider-prev',
42499                             cn : [
42500                                 {
42501                                     tag : 'i',
42502                                     cls : 'fa fa-chevron-left'
42503                                 }
42504                             ]
42505                         },
42506                         {
42507                             tag : 'div',
42508                             cls : 'roo-document-slider-thumb',
42509                             cn : [
42510                                 {
42511                                     tag : 'img',
42512                                     cls : 'roo-document-slider-image'
42513                                 }
42514                             ]
42515                         },
42516                         {
42517                             tag : 'div',
42518                             cls : 'roo-document-slider-next',
42519                             cn : [
42520                                 {
42521                                     tag : 'i',
42522                                     cls : 'fa fa-chevron-right'
42523                                 }
42524                             ]
42525                         }
42526                     ]
42527                 }
42528             ]
42529         };
42530         
42531         return cfg;
42532     },
42533     
42534     initEvents : function()
42535     {
42536         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
42537         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
42538         
42539         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
42540         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
42541         
42542         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
42543         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
42544         
42545         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
42546         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
42547         
42548         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
42549         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
42550         
42551         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
42552         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
42553         
42554         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
42555         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
42556         
42557         this.thumbEl.on('click', this.onClick, this);
42558         
42559         this.prevIndicator.on('click', this.prev, this);
42560         
42561         this.nextIndicator.on('click', this.next, this);
42562         
42563     },
42564     
42565     initial : function()
42566     {
42567         if(this.files.length){
42568             this.indicator = 1;
42569             this.update()
42570         }
42571         
42572         this.fireEvent('initial', this);
42573     },
42574     
42575     update : function()
42576     {
42577         this.imageEl.attr('src', this.files[this.indicator - 1]);
42578         
42579         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42580         
42581         this.prevIndicator.show();
42582         
42583         if(this.indicator == 1){
42584             this.prevIndicator.hide();
42585         }
42586         
42587         this.nextIndicator.show();
42588         
42589         if(this.indicator == this.files.length){
42590             this.nextIndicator.hide();
42591         }
42592         
42593         this.thumbEl.scrollTo('top');
42594         
42595         this.fireEvent('update', this);
42596     },
42597     
42598     onClick : function(e)
42599     {
42600         e.preventDefault();
42601         
42602         this.fireEvent('click', this);
42603     },
42604     
42605     prev : function(e)
42606     {
42607         e.preventDefault();
42608         
42609         this.indicator = Math.max(1, this.indicator - 1);
42610         
42611         this.update();
42612     },
42613     
42614     next : function(e)
42615     {
42616         e.preventDefault();
42617         
42618         this.indicator = Math.min(this.files.length, this.indicator + 1);
42619         
42620         this.update();
42621     }
42622 });
42623 /*
42624  * - LGPL
42625  *
42626  * RadioSet
42627  *
42628  *
42629  */
42630
42631 /**
42632  * @class Roo.bootstrap.form.RadioSet
42633  * @extends Roo.bootstrap.form.Input
42634  * @children Roo.bootstrap.form.Radio
42635  * Bootstrap RadioSet class
42636  * @cfg {String} indicatorpos (left|right) default left
42637  * @cfg {Boolean} inline (true|false) inline the element (default true)
42638  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42639  * @constructor
42640  * Create a new RadioSet
42641  * @param {Object} config The config object
42642  */
42643
42644 Roo.bootstrap.form.RadioSet = function(config){
42645     
42646     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42647     
42648     this.radioes = [];
42649     
42650     Roo.bootstrap.form.RadioSet.register(this);
42651     
42652     this.addEvents({
42653         /**
42654         * @event check
42655         * Fires when the element is checked or unchecked.
42656         * @param {Roo.bootstrap.form.RadioSet} this This radio
42657         * @param {Roo.bootstrap.form.Radio} item The checked item
42658         */
42659        check : true,
42660        /**
42661         * @event click
42662         * Fires when the element is click.
42663         * @param {Roo.bootstrap.form.RadioSet} this This radio set
42664         * @param {Roo.bootstrap.form.Radio} item The checked item
42665         * @param {Roo.EventObject} e The event object
42666         */
42667        click : true
42668     });
42669     
42670 };
42671
42672 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
42673
42674     radioes : false,
42675     
42676     inline : true,
42677     
42678     weight : '',
42679     
42680     indicatorpos : 'left',
42681     
42682     getAutoCreate : function()
42683     {
42684         var label = {
42685             tag : 'label',
42686             cls : 'roo-radio-set-label',
42687             cn : [
42688                 {
42689                     tag : 'span',
42690                     html : this.fieldLabel
42691                 }
42692             ]
42693         };
42694         if (Roo.bootstrap.version == 3) {
42695             
42696             
42697             if(this.indicatorpos == 'left'){
42698                 label.cn.unshift({
42699                     tag : 'i',
42700                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42701                     tooltip : 'This field is required'
42702                 });
42703             } else {
42704                 label.cn.push({
42705                     tag : 'i',
42706                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42707                     tooltip : 'This field is required'
42708                 });
42709             }
42710         }
42711         var items = {
42712             tag : 'div',
42713             cls : 'roo-radio-set-items'
42714         };
42715         
42716         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42717         
42718         if (align === 'left' && this.fieldLabel.length) {
42719             
42720             items = {
42721                 cls : "roo-radio-set-right", 
42722                 cn: [
42723                     items
42724                 ]
42725             };
42726             
42727             if(this.labelWidth > 12){
42728                 label.style = "width: " + this.labelWidth + 'px';
42729             }
42730             
42731             if(this.labelWidth < 13 && this.labelmd == 0){
42732                 this.labelmd = this.labelWidth;
42733             }
42734             
42735             if(this.labellg > 0){
42736                 label.cls += ' col-lg-' + this.labellg;
42737                 items.cls += ' col-lg-' + (12 - this.labellg);
42738             }
42739             
42740             if(this.labelmd > 0){
42741                 label.cls += ' col-md-' + this.labelmd;
42742                 items.cls += ' col-md-' + (12 - this.labelmd);
42743             }
42744             
42745             if(this.labelsm > 0){
42746                 label.cls += ' col-sm-' + this.labelsm;
42747                 items.cls += ' col-sm-' + (12 - this.labelsm);
42748             }
42749             
42750             if(this.labelxs > 0){
42751                 label.cls += ' col-xs-' + this.labelxs;
42752                 items.cls += ' col-xs-' + (12 - this.labelxs);
42753             }
42754         }
42755         
42756         var cfg = {
42757             tag : 'div',
42758             cls : 'roo-radio-set',
42759             cn : [
42760                 {
42761                     tag : 'input',
42762                     cls : 'roo-radio-set-input',
42763                     type : 'hidden',
42764                     name : this.name,
42765                     value : this.value ? this.value :  ''
42766                 },
42767                 label,
42768                 items
42769             ]
42770         };
42771         
42772         if(this.weight.length){
42773             cfg.cls += ' roo-radio-' + this.weight;
42774         }
42775         
42776         if(this.inline) {
42777             cfg.cls += ' roo-radio-set-inline';
42778         }
42779         
42780         var settings=this;
42781         ['xs','sm','md','lg'].map(function(size){
42782             if (settings[size]) {
42783                 cfg.cls += ' col-' + size + '-' + settings[size];
42784             }
42785         });
42786         
42787         return cfg;
42788         
42789     },
42790
42791     initEvents : function()
42792     {
42793         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42794         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42795         
42796         if(!this.fieldLabel.length){
42797             this.labelEl.hide();
42798         }
42799         
42800         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42801         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42802         
42803         this.indicator = this.indicatorEl();
42804         
42805         if(this.indicator){
42806             this.indicator.addClass('invisible');
42807         }
42808         
42809         this.originalValue = this.getValue();
42810         
42811     },
42812     
42813     inputEl: function ()
42814     {
42815         return this.el.select('.roo-radio-set-input', true).first();
42816     },
42817     
42818     getChildContainer : function()
42819     {
42820         return this.itemsEl;
42821     },
42822     
42823     register : function(item)
42824     {
42825         this.radioes.push(item);
42826         
42827     },
42828     
42829     validate : function()
42830     {   
42831         if(this.getVisibilityEl().hasClass('hidden')){
42832             return true;
42833         }
42834         
42835         var valid = false;
42836         
42837         Roo.each(this.radioes, function(i){
42838             if(!i.checked){
42839                 return;
42840             }
42841             
42842             valid = true;
42843             return false;
42844         });
42845         
42846         if(this.allowBlank) {
42847             return true;
42848         }
42849         
42850         if(this.disabled || valid){
42851             this.markValid();
42852             return true;
42853         }
42854         
42855         this.markInvalid();
42856         return false;
42857         
42858     },
42859     
42860     markValid : function()
42861     {
42862         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42863             this.indicatorEl().removeClass('visible');
42864             this.indicatorEl().addClass('invisible');
42865         }
42866         
42867         
42868         if (Roo.bootstrap.version == 3) {
42869             this.el.removeClass([this.invalidClass, this.validClass]);
42870             this.el.addClass(this.validClass);
42871         } else {
42872             this.el.removeClass(['is-invalid','is-valid']);
42873             this.el.addClass(['is-valid']);
42874         }
42875         this.fireEvent('valid', this);
42876     },
42877     
42878     markInvalid : function(msg)
42879     {
42880         if(this.allowBlank || this.disabled){
42881             return;
42882         }
42883         
42884         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42885             this.indicatorEl().removeClass('invisible');
42886             this.indicatorEl().addClass('visible');
42887         }
42888         if (Roo.bootstrap.version == 3) {
42889             this.el.removeClass([this.invalidClass, this.validClass]);
42890             this.el.addClass(this.invalidClass);
42891         } else {
42892             this.el.removeClass(['is-invalid','is-valid']);
42893             this.el.addClass(['is-invalid']);
42894         }
42895         
42896         this.fireEvent('invalid', this, msg);
42897         
42898     },
42899     
42900     setValue : function(v, suppressEvent)
42901     {   
42902         if(this.value === v){
42903             return;
42904         }
42905         
42906         this.value = v;
42907         
42908         if(this.rendered){
42909             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42910         }
42911         
42912         Roo.each(this.radioes, function(i){
42913             i.checked = false;
42914             i.el.removeClass('checked');
42915         });
42916         
42917         Roo.each(this.radioes, function(i){
42918             
42919             if(i.value === v || i.value.toString() === v.toString()){
42920                 i.checked = true;
42921                 i.el.addClass('checked');
42922                 
42923                 if(suppressEvent !== true){
42924                     this.fireEvent('check', this, i);
42925                 }
42926                 
42927                 return false;
42928             }
42929             
42930         }, this);
42931         
42932         this.validate();
42933     },
42934     
42935     clearInvalid : function(){
42936         
42937         if(!this.el || this.preventMark){
42938             return;
42939         }
42940         
42941         this.el.removeClass([this.invalidClass]);
42942         
42943         this.fireEvent('valid', this);
42944     }
42945     
42946 });
42947
42948 Roo.apply(Roo.bootstrap.form.RadioSet, {
42949     
42950     groups: {},
42951     
42952     register : function(set)
42953     {
42954         this.groups[set.name] = set;
42955     },
42956     
42957     get: function(name) 
42958     {
42959         if (typeof(this.groups[name]) == 'undefined') {
42960             return false;
42961         }
42962         
42963         return this.groups[name] ;
42964     }
42965     
42966 });
42967 /*
42968  * Based on:
42969  * Ext JS Library 1.1.1
42970  * Copyright(c) 2006-2007, Ext JS, LLC.
42971  *
42972  * Originally Released Under LGPL - original licence link has changed is not relivant.
42973  *
42974  * Fork - LGPL
42975  * <script type="text/javascript">
42976  */
42977
42978
42979 /**
42980  * @class Roo.bootstrap.SplitBar
42981  * @extends Roo.util.Observable
42982  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42983  * <br><br>
42984  * Usage:
42985  * <pre><code>
42986 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42987                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42988 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42989 split.minSize = 100;
42990 split.maxSize = 600;
42991 split.animate = true;
42992 split.on('moved', splitterMoved);
42993 </code></pre>
42994  * @constructor
42995  * Create a new SplitBar
42996  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42997  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42998  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42999  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
43000                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
43001                         position of the SplitBar).
43002  */
43003 Roo.bootstrap.SplitBar = function(cfg){
43004     
43005     /** @private */
43006     
43007     //{
43008     //  dragElement : elm
43009     //  resizingElement: el,
43010         // optional..
43011     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
43012     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
43013         // existingProxy ???
43014     //}
43015     
43016     this.el = Roo.get(cfg.dragElement, true);
43017     this.el.dom.unselectable = "on";
43018     /** @private */
43019     this.resizingEl = Roo.get(cfg.resizingElement, true);
43020
43021     /**
43022      * @private
43023      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
43024      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
43025      * @type Number
43026      */
43027     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
43028     
43029     /**
43030      * The minimum size of the resizing element. (Defaults to 0)
43031      * @type Number
43032      */
43033     this.minSize = 0;
43034     
43035     /**
43036      * The maximum size of the resizing element. (Defaults to 2000)
43037      * @type Number
43038      */
43039     this.maxSize = 2000;
43040     
43041     /**
43042      * Whether to animate the transition to the new size
43043      * @type Boolean
43044      */
43045     this.animate = false;
43046     
43047     /**
43048      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
43049      * @type Boolean
43050      */
43051     this.useShim = false;
43052     
43053     /** @private */
43054     this.shim = null;
43055     
43056     if(!cfg.existingProxy){
43057         /** @private */
43058         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
43059     }else{
43060         this.proxy = Roo.get(cfg.existingProxy).dom;
43061     }
43062     /** @private */
43063     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
43064     
43065     /** @private */
43066     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
43067     
43068     /** @private */
43069     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
43070     
43071     /** @private */
43072     this.dragSpecs = {};
43073     
43074     /**
43075      * @private The adapter to use to positon and resize elements
43076      */
43077     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
43078     this.adapter.init(this);
43079     
43080     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
43081         /** @private */
43082         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
43083         this.el.addClass("roo-splitbar-h");
43084     }else{
43085         /** @private */
43086         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
43087         this.el.addClass("roo-splitbar-v");
43088     }
43089     
43090     this.addEvents({
43091         /**
43092          * @event resize
43093          * Fires when the splitter is moved (alias for {@link #event-moved})
43094          * @param {Roo.bootstrap.SplitBar} this
43095          * @param {Number} newSize the new width or height
43096          */
43097         "resize" : true,
43098         /**
43099          * @event moved
43100          * Fires when the splitter is moved
43101          * @param {Roo.bootstrap.SplitBar} this
43102          * @param {Number} newSize the new width or height
43103          */
43104         "moved" : true,
43105         /**
43106          * @event beforeresize
43107          * Fires before the splitter is dragged
43108          * @param {Roo.bootstrap.SplitBar} this
43109          */
43110         "beforeresize" : true,
43111
43112         "beforeapply" : true
43113     });
43114
43115     Roo.util.Observable.call(this);
43116 };
43117
43118 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
43119     onStartProxyDrag : function(x, y){
43120         this.fireEvent("beforeresize", this);
43121         if(!this.overlay){
43122             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
43123             o.unselectable();
43124             o.enableDisplayMode("block");
43125             // all splitbars share the same overlay
43126             Roo.bootstrap.SplitBar.prototype.overlay = o;
43127         }
43128         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
43129         this.overlay.show();
43130         Roo.get(this.proxy).setDisplayed("block");
43131         var size = this.adapter.getElementSize(this);
43132         this.activeMinSize = this.getMinimumSize();;
43133         this.activeMaxSize = this.getMaximumSize();;
43134         var c1 = size - this.activeMinSize;
43135         var c2 = Math.max(this.activeMaxSize - size, 0);
43136         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
43137             this.dd.resetConstraints();
43138             this.dd.setXConstraint(
43139                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
43140                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
43141             );
43142             this.dd.setYConstraint(0, 0);
43143         }else{
43144             this.dd.resetConstraints();
43145             this.dd.setXConstraint(0, 0);
43146             this.dd.setYConstraint(
43147                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
43148                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
43149             );
43150          }
43151         this.dragSpecs.startSize = size;
43152         this.dragSpecs.startPoint = [x, y];
43153         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
43154     },
43155     
43156     /** 
43157      * @private Called after the drag operation by the DDProxy
43158      */
43159     onEndProxyDrag : function(e){
43160         Roo.get(this.proxy).setDisplayed(false);
43161         var endPoint = Roo.lib.Event.getXY(e);
43162         if(this.overlay){
43163             this.overlay.hide();
43164         }
43165         var newSize;
43166         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
43167             newSize = this.dragSpecs.startSize + 
43168                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
43169                     endPoint[0] - this.dragSpecs.startPoint[0] :
43170                     this.dragSpecs.startPoint[0] - endPoint[0]
43171                 );
43172         }else{
43173             newSize = this.dragSpecs.startSize + 
43174                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
43175                     endPoint[1] - this.dragSpecs.startPoint[1] :
43176                     this.dragSpecs.startPoint[1] - endPoint[1]
43177                 );
43178         }
43179         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
43180         if(newSize != this.dragSpecs.startSize){
43181             if(this.fireEvent('beforeapply', this, newSize) !== false){
43182                 this.adapter.setElementSize(this, newSize);
43183                 this.fireEvent("moved", this, newSize);
43184                 this.fireEvent("resize", this, newSize);
43185             }
43186         }
43187     },
43188     
43189     /**
43190      * Get the adapter this SplitBar uses
43191      * @return The adapter object
43192      */
43193     getAdapter : function(){
43194         return this.adapter;
43195     },
43196     
43197     /**
43198      * Set the adapter this SplitBar uses
43199      * @param {Object} adapter A SplitBar adapter object
43200      */
43201     setAdapter : function(adapter){
43202         this.adapter = adapter;
43203         this.adapter.init(this);
43204     },
43205     
43206     /**
43207      * Gets the minimum size for the resizing element
43208      * @return {Number} The minimum size
43209      */
43210     getMinimumSize : function(){
43211         return this.minSize;
43212     },
43213     
43214     /**
43215      * Sets the minimum size for the resizing element
43216      * @param {Number} minSize The minimum size
43217      */
43218     setMinimumSize : function(minSize){
43219         this.minSize = minSize;
43220     },
43221     
43222     /**
43223      * Gets the maximum size for the resizing element
43224      * @return {Number} The maximum size
43225      */
43226     getMaximumSize : function(){
43227         return this.maxSize;
43228     },
43229     
43230     /**
43231      * Sets the maximum size for the resizing element
43232      * @param {Number} maxSize The maximum size
43233      */
43234     setMaximumSize : function(maxSize){
43235         this.maxSize = maxSize;
43236     },
43237     
43238     /**
43239      * Sets the initialize size for the resizing element
43240      * @param {Number} size The initial size
43241      */
43242     setCurrentSize : function(size){
43243         var oldAnimate = this.animate;
43244         this.animate = false;
43245         this.adapter.setElementSize(this, size);
43246         this.animate = oldAnimate;
43247     },
43248     
43249     /**
43250      * Destroy this splitbar. 
43251      * @param {Boolean} removeEl True to remove the element
43252      */
43253     destroy : function(removeEl){
43254         if(this.shim){
43255             this.shim.remove();
43256         }
43257         this.dd.unreg();
43258         this.proxy.parentNode.removeChild(this.proxy);
43259         if(removeEl){
43260             this.el.remove();
43261         }
43262     }
43263 });
43264
43265 /**
43266  * @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.
43267  */
43268 Roo.bootstrap.SplitBar.createProxy = function(dir){
43269     var proxy = new Roo.Element(document.createElement("div"));
43270     proxy.unselectable();
43271     var cls = 'roo-splitbar-proxy';
43272     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
43273     document.body.appendChild(proxy.dom);
43274     return proxy.dom;
43275 };
43276
43277 /** 
43278  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
43279  * Default Adapter. It assumes the splitter and resizing element are not positioned
43280  * elements and only gets/sets the width of the element. Generally used for table based layouts.
43281  */
43282 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
43283 };
43284
43285 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
43286     // do nothing for now
43287     init : function(s){
43288     
43289     },
43290     /**
43291      * Called before drag operations to get the current size of the resizing element. 
43292      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
43293      */
43294      getElementSize : function(s){
43295         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
43296             return s.resizingEl.getWidth();
43297         }else{
43298             return s.resizingEl.getHeight();
43299         }
43300     },
43301     
43302     /**
43303      * Called after drag operations to set the size of the resizing element.
43304      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
43305      * @param {Number} newSize The new size to set
43306      * @param {Function} onComplete A function to be invoked when resizing is complete
43307      */
43308     setElementSize : function(s, newSize, onComplete){
43309         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
43310             if(!s.animate){
43311                 s.resizingEl.setWidth(newSize);
43312                 if(onComplete){
43313                     onComplete(s, newSize);
43314                 }
43315             }else{
43316                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
43317             }
43318         }else{
43319             
43320             if(!s.animate){
43321                 s.resizingEl.setHeight(newSize);
43322                 if(onComplete){
43323                     onComplete(s, newSize);
43324                 }
43325             }else{
43326                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
43327             }
43328         }
43329     }
43330 };
43331
43332 /** 
43333  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
43334  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
43335  * Adapter that  moves the splitter element to align with the resized sizing element. 
43336  * Used with an absolute positioned SplitBar.
43337  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
43338  * document.body, make sure you assign an id to the body element.
43339  */
43340 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
43341     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
43342     this.container = Roo.get(container);
43343 };
43344
43345 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
43346     init : function(s){
43347         this.basic.init(s);
43348     },
43349     
43350     getElementSize : function(s){
43351         return this.basic.getElementSize(s);
43352     },
43353     
43354     setElementSize : function(s, newSize, onComplete){
43355         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
43356     },
43357     
43358     moveSplitter : function(s){
43359         var yes = Roo.bootstrap.SplitBar;
43360         switch(s.placement){
43361             case yes.LEFT:
43362                 s.el.setX(s.resizingEl.getRight());
43363                 break;
43364             case yes.RIGHT:
43365                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
43366                 break;
43367             case yes.TOP:
43368                 s.el.setY(s.resizingEl.getBottom());
43369                 break;
43370             case yes.BOTTOM:
43371                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
43372                 break;
43373         }
43374     }
43375 };
43376
43377 /**
43378  * Orientation constant - Create a vertical SplitBar
43379  * @static
43380  * @type Number
43381  */
43382 Roo.bootstrap.SplitBar.VERTICAL = 1;
43383
43384 /**
43385  * Orientation constant - Create a horizontal SplitBar
43386  * @static
43387  * @type Number
43388  */
43389 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
43390
43391 /**
43392  * Placement constant - The resizing element is to the left of the splitter element
43393  * @static
43394  * @type Number
43395  */
43396 Roo.bootstrap.SplitBar.LEFT = 1;
43397
43398 /**
43399  * Placement constant - The resizing element is to the right of the splitter element
43400  * @static
43401  * @type Number
43402  */
43403 Roo.bootstrap.SplitBar.RIGHT = 2;
43404
43405 /**
43406  * Placement constant - The resizing element is positioned above the splitter element
43407  * @static
43408  * @type Number
43409  */
43410 Roo.bootstrap.SplitBar.TOP = 3;
43411
43412 /**
43413  * Placement constant - The resizing element is positioned under splitter element
43414  * @static
43415  * @type Number
43416  */
43417 Roo.bootstrap.SplitBar.BOTTOM = 4;
43418 /*
43419  * Based on:
43420  * Ext JS Library 1.1.1
43421  * Copyright(c) 2006-2007, Ext JS, LLC.
43422  *
43423  * Originally Released Under LGPL - original licence link has changed is not relivant.
43424  *
43425  * Fork - LGPL
43426  * <script type="text/javascript">
43427  */
43428
43429 /**
43430  * @class Roo.bootstrap.layout.Manager
43431  * @extends Roo.bootstrap.Component
43432  * @abstract
43433  * Base class for layout managers.
43434  */
43435 Roo.bootstrap.layout.Manager = function(config)
43436 {
43437     this.monitorWindowResize = true; // do this before we apply configuration.
43438     
43439     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
43440
43441
43442
43443
43444
43445     /** false to disable window resize monitoring @type Boolean */
43446     
43447     this.regions = {};
43448     this.addEvents({
43449         /**
43450          * @event layout
43451          * Fires when a layout is performed.
43452          * @param {Roo.LayoutManager} this
43453          */
43454         "layout" : true,
43455         /**
43456          * @event regionresized
43457          * Fires when the user resizes a region.
43458          * @param {Roo.LayoutRegion} region The resized region
43459          * @param {Number} newSize The new size (width for east/west, height for north/south)
43460          */
43461         "regionresized" : true,
43462         /**
43463          * @event regioncollapsed
43464          * Fires when a region is collapsed.
43465          * @param {Roo.LayoutRegion} region The collapsed region
43466          */
43467         "regioncollapsed" : true,
43468         /**
43469          * @event regionexpanded
43470          * Fires when a region is expanded.
43471          * @param {Roo.LayoutRegion} region The expanded region
43472          */
43473         "regionexpanded" : true
43474     });
43475     this.updating = false;
43476
43477     if (config.el) {
43478         this.el = Roo.get(config.el);
43479         this.initEvents();
43480     }
43481
43482 };
43483
43484 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
43485
43486
43487     regions : null,
43488
43489     monitorWindowResize : true,
43490
43491
43492     updating : false,
43493
43494
43495     onRender : function(ct, position)
43496     {
43497         if(!this.el){
43498             this.el = Roo.get(ct);
43499             this.initEvents();
43500         }
43501         //this.fireEvent('render',this);
43502     },
43503
43504
43505     initEvents: function()
43506     {
43507
43508
43509         // ie scrollbar fix
43510         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43511             document.body.scroll = "no";
43512         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43513             this.el.position('relative');
43514         }
43515         this.id = this.el.id;
43516         this.el.addClass("roo-layout-container");
43517         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43518         if(this.el.dom != document.body ) {
43519             this.el.on('resize', this.layout,this);
43520             this.el.on('show', this.layout,this);
43521         }
43522
43523     },
43524
43525     /**
43526      * Returns true if this layout is currently being updated
43527      * @return {Boolean}
43528      */
43529     isUpdating : function(){
43530         return this.updating;
43531     },
43532
43533     /**
43534      * Suspend the LayoutManager from doing auto-layouts while
43535      * making multiple add or remove calls
43536      */
43537     beginUpdate : function(){
43538         this.updating = true;
43539     },
43540
43541     /**
43542      * Restore auto-layouts and optionally disable the manager from performing a layout
43543      * @param {Boolean} noLayout true to disable a layout update
43544      */
43545     endUpdate : function(noLayout){
43546         this.updating = false;
43547         if(!noLayout){
43548             this.layout();
43549         }
43550     },
43551
43552     layout: function(){
43553         // abstract...
43554     },
43555
43556     onRegionResized : function(region, newSize){
43557         this.fireEvent("regionresized", region, newSize);
43558         this.layout();
43559     },
43560
43561     onRegionCollapsed : function(region){
43562         this.fireEvent("regioncollapsed", region);
43563     },
43564
43565     onRegionExpanded : function(region){
43566         this.fireEvent("regionexpanded", region);
43567     },
43568
43569     /**
43570      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43571      * performs box-model adjustments.
43572      * @return {Object} The size as an object {width: (the width), height: (the height)}
43573      */
43574     getViewSize : function()
43575     {
43576         var size;
43577         if(this.el.dom != document.body){
43578             size = this.el.getSize();
43579         }else{
43580             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43581         }
43582         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43583         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43584         return size;
43585     },
43586
43587     /**
43588      * Returns the Element this layout is bound to.
43589      * @return {Roo.Element}
43590      */
43591     getEl : function(){
43592         return this.el;
43593     },
43594
43595     /**
43596      * Returns the specified region.
43597      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43598      * @return {Roo.LayoutRegion}
43599      */
43600     getRegion : function(target){
43601         return this.regions[target.toLowerCase()];
43602     },
43603
43604     onWindowResize : function(){
43605         if(this.monitorWindowResize){
43606             this.layout();
43607         }
43608     }
43609 });
43610 /*
43611  * Based on:
43612  * Ext JS Library 1.1.1
43613  * Copyright(c) 2006-2007, Ext JS, LLC.
43614  *
43615  * Originally Released Under LGPL - original licence link has changed is not relivant.
43616  *
43617  * Fork - LGPL
43618  * <script type="text/javascript">
43619  */
43620 /**
43621  * @class Roo.bootstrap.layout.Border
43622  * @extends Roo.bootstrap.layout.Manager
43623  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43624  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43625  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43626  * please see: examples/bootstrap/nested.html<br><br>
43627  
43628 <b>The container the layout is rendered into can be either the body element or any other element.
43629 If it is not the body element, the container needs to either be an absolute positioned element,
43630 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43631 the container size if it is not the body element.</b>
43632
43633 * @constructor
43634 * Create a new Border
43635 * @param {Object} config Configuration options
43636  */
43637 Roo.bootstrap.layout.Border = function(config){
43638     config = config || {};
43639     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43640     
43641     
43642     
43643     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43644         if(config[region]){
43645             config[region].region = region;
43646             this.addRegion(config[region]);
43647         }
43648     },this);
43649     
43650 };
43651
43652 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
43653
43654 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43655     
43656         /**
43657          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43658          */
43659         /**
43660          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43661          */
43662         /**
43663          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43664          */
43665         /**
43666          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43667          */
43668         /**
43669          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43670          */
43671         
43672         
43673         
43674         
43675     parent : false, // this might point to a 'nest' or a ???
43676     
43677     /**
43678      * Creates and adds a new region if it doesn't already exist.
43679      * @param {String} target The target region key (north, south, east, west or center).
43680      * @param {Object} config The regions config object
43681      * @return {BorderLayoutRegion} The new region
43682      */
43683     addRegion : function(config)
43684     {
43685         if(!this.regions[config.region]){
43686             var r = this.factory(config);
43687             this.bindRegion(r);
43688         }
43689         return this.regions[config.region];
43690     },
43691
43692     // private (kinda)
43693     bindRegion : function(r){
43694         this.regions[r.config.region] = r;
43695         
43696         r.on("visibilitychange",    this.layout, this);
43697         r.on("paneladded",          this.layout, this);
43698         r.on("panelremoved",        this.layout, this);
43699         r.on("invalidated",         this.layout, this);
43700         r.on("resized",             this.onRegionResized, this);
43701         r.on("collapsed",           this.onRegionCollapsed, this);
43702         r.on("expanded",            this.onRegionExpanded, this);
43703     },
43704
43705     /**
43706      * Performs a layout update.
43707      */
43708     layout : function()
43709     {
43710         if(this.updating) {
43711             return;
43712         }
43713         
43714         // render all the rebions if they have not been done alreayd?
43715         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43716             if(this.regions[region] && !this.regions[region].bodyEl){
43717                 this.regions[region].onRender(this.el)
43718             }
43719         },this);
43720         
43721         var size = this.getViewSize();
43722         var w = size.width;
43723         var h = size.height;
43724         var centerW = w;
43725         var centerH = h;
43726         var centerY = 0;
43727         var centerX = 0;
43728         //var x = 0, y = 0;
43729
43730         var rs = this.regions;
43731         var north = rs["north"];
43732         var south = rs["south"]; 
43733         var west = rs["west"];
43734         var east = rs["east"];
43735         var center = rs["center"];
43736         //if(this.hideOnLayout){ // not supported anymore
43737             //c.el.setStyle("display", "none");
43738         //}
43739         if(north && north.isVisible()){
43740             var b = north.getBox();
43741             var m = north.getMargins();
43742             b.width = w - (m.left+m.right);
43743             b.x = m.left;
43744             b.y = m.top;
43745             centerY = b.height + b.y + m.bottom;
43746             centerH -= centerY;
43747             north.updateBox(this.safeBox(b));
43748         }
43749         if(south && south.isVisible()){
43750             var b = south.getBox();
43751             var m = south.getMargins();
43752             b.width = w - (m.left+m.right);
43753             b.x = m.left;
43754             var totalHeight = (b.height + m.top + m.bottom);
43755             b.y = h - totalHeight + m.top;
43756             centerH -= totalHeight;
43757             south.updateBox(this.safeBox(b));
43758         }
43759         if(west && west.isVisible()){
43760             var b = west.getBox();
43761             var m = west.getMargins();
43762             b.height = centerH - (m.top+m.bottom);
43763             b.x = m.left;
43764             b.y = centerY + m.top;
43765             var totalWidth = (b.width + m.left + m.right);
43766             centerX += totalWidth;
43767             centerW -= totalWidth;
43768             west.updateBox(this.safeBox(b));
43769         }
43770         if(east && east.isVisible()){
43771             var b = east.getBox();
43772             var m = east.getMargins();
43773             b.height = centerH - (m.top+m.bottom);
43774             var totalWidth = (b.width + m.left + m.right);
43775             b.x = w - totalWidth + m.left;
43776             b.y = centerY + m.top;
43777             centerW -= totalWidth;
43778             east.updateBox(this.safeBox(b));
43779         }
43780         if(center){
43781             var m = center.getMargins();
43782             var centerBox = {
43783                 x: centerX + m.left,
43784                 y: centerY + m.top,
43785                 width: centerW - (m.left+m.right),
43786                 height: centerH - (m.top+m.bottom)
43787             };
43788             //if(this.hideOnLayout){
43789                 //center.el.setStyle("display", "block");
43790             //}
43791             center.updateBox(this.safeBox(centerBox));
43792         }
43793         this.el.repaint();
43794         this.fireEvent("layout", this);
43795     },
43796
43797     // private
43798     safeBox : function(box){
43799         box.width = Math.max(0, box.width);
43800         box.height = Math.max(0, box.height);
43801         return box;
43802     },
43803
43804     /**
43805      * Adds a ContentPanel (or subclass) to this layout.
43806      * @param {String} target The target region key (north, south, east, west or center).
43807      * @param {Roo.ContentPanel} panel The panel to add
43808      * @return {Roo.ContentPanel} The added panel
43809      */
43810     add : function(target, panel){
43811          
43812         target = target.toLowerCase();
43813         return this.regions[target].add(panel);
43814     },
43815
43816     /**
43817      * Remove a ContentPanel (or subclass) to this layout.
43818      * @param {String} target The target region key (north, south, east, west or center).
43819      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43820      * @return {Roo.ContentPanel} The removed panel
43821      */
43822     remove : function(target, panel){
43823         target = target.toLowerCase();
43824         return this.regions[target].remove(panel);
43825     },
43826
43827     /**
43828      * Searches all regions for a panel with the specified id
43829      * @param {String} panelId
43830      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43831      */
43832     findPanel : function(panelId){
43833         var rs = this.regions;
43834         for(var target in rs){
43835             if(typeof rs[target] != "function"){
43836                 var p = rs[target].getPanel(panelId);
43837                 if(p){
43838                     return p;
43839                 }
43840             }
43841         }
43842         return null;
43843     },
43844
43845     /**
43846      * Searches all regions for a panel with the specified id and activates (shows) it.
43847      * @param {String/ContentPanel} panelId The panels id or the panel itself
43848      * @return {Roo.ContentPanel} The shown panel or null
43849      */
43850     showPanel : function(panelId) {
43851       var rs = this.regions;
43852       for(var target in rs){
43853          var r = rs[target];
43854          if(typeof r != "function"){
43855             if(r.hasPanel(panelId)){
43856                return r.showPanel(panelId);
43857             }
43858          }
43859       }
43860       return null;
43861    },
43862
43863    /**
43864      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43865      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43866      */
43867    /*
43868     restoreState : function(provider){
43869         if(!provider){
43870             provider = Roo.state.Manager;
43871         }
43872         var sm = new Roo.LayoutStateManager();
43873         sm.init(this, provider);
43874     },
43875 */
43876  
43877  
43878     /**
43879      * Adds a xtype elements to the layout.
43880      * <pre><code>
43881
43882 layout.addxtype({
43883        xtype : 'ContentPanel',
43884        region: 'west',
43885        items: [ .... ]
43886    }
43887 );
43888
43889 layout.addxtype({
43890         xtype : 'NestedLayoutPanel',
43891         region: 'west',
43892         layout: {
43893            center: { },
43894            west: { }   
43895         },
43896         items : [ ... list of content panels or nested layout panels.. ]
43897    }
43898 );
43899 </code></pre>
43900      * @param {Object} cfg Xtype definition of item to add.
43901      */
43902     addxtype : function(cfg)
43903     {
43904         // basically accepts a pannel...
43905         // can accept a layout region..!?!?
43906         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43907         
43908         
43909         // theory?  children can only be panels??
43910         
43911         //if (!cfg.xtype.match(/Panel$/)) {
43912         //    return false;
43913         //}
43914         var ret = false;
43915         
43916         if (typeof(cfg.region) == 'undefined') {
43917             Roo.log("Failed to add Panel, region was not set");
43918             Roo.log(cfg);
43919             return false;
43920         }
43921         var region = cfg.region;
43922         delete cfg.region;
43923         
43924           
43925         var xitems = [];
43926         if (cfg.items) {
43927             xitems = cfg.items;
43928             delete cfg.items;
43929         }
43930         var nb = false;
43931         
43932         if ( region == 'center') {
43933             Roo.log("Center: " + cfg.title);
43934         }
43935         
43936         
43937         switch(cfg.xtype) 
43938         {
43939             case 'Content':  // ContentPanel (el, cfg)
43940             case 'Scroll':  // ContentPanel (el, cfg)
43941             case 'View': 
43942                 cfg.autoCreate = cfg.autoCreate || true;
43943                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43944                 //} else {
43945                 //    var el = this.el.createChild();
43946                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43947                 //}
43948                 
43949                 this.add(region, ret);
43950                 break;
43951             
43952             /*
43953             case 'TreePanel': // our new panel!
43954                 cfg.el = this.el.createChild();
43955                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43956                 this.add(region, ret);
43957                 break;
43958             */
43959             
43960             case 'Nest': 
43961                 // create a new Layout (which is  a Border Layout...
43962                 
43963                 var clayout = cfg.layout;
43964                 clayout.el  = this.el.createChild();
43965                 clayout.items   = clayout.items  || [];
43966                 
43967                 delete cfg.layout;
43968                 
43969                 // replace this exitems with the clayout ones..
43970                 xitems = clayout.items;
43971                  
43972                 // force background off if it's in center...
43973                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43974                     cfg.background = false;
43975                 }
43976                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43977                 
43978                 
43979                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43980                 //console.log('adding nested layout panel '  + cfg.toSource());
43981                 this.add(region, ret);
43982                 nb = {}; /// find first...
43983                 break;
43984             
43985             case 'Grid':
43986                 
43987                 // needs grid and region
43988                 
43989                 //var el = this.getRegion(region).el.createChild();
43990                 /*
43991                  *var el = this.el.createChild();
43992                 // create the grid first...
43993                 cfg.grid.container = el;
43994                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43995                 */
43996                 
43997                 if (region == 'center' && this.active ) {
43998                     cfg.background = false;
43999                 }
44000                 
44001                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
44002                 
44003                 this.add(region, ret);
44004                 /*
44005                 if (cfg.background) {
44006                     // render grid on panel activation (if panel background)
44007                     ret.on('activate', function(gp) {
44008                         if (!gp.grid.rendered) {
44009                     //        gp.grid.render(el);
44010                         }
44011                     });
44012                 } else {
44013                   //  cfg.grid.render(el);
44014                 }
44015                 */
44016                 break;
44017            
44018            
44019             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
44020                 // it was the old xcomponent building that caused this before.
44021                 // espeically if border is the top element in the tree.
44022                 ret = this;
44023                 break; 
44024                 
44025                     
44026                 
44027                 
44028                 
44029             default:
44030                 /*
44031                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
44032                     
44033                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44034                     this.add(region, ret);
44035                 } else {
44036                 */
44037                     Roo.log(cfg);
44038                     throw "Can not add '" + cfg.xtype + "' to Border";
44039                     return null;
44040              
44041                                 
44042              
44043         }
44044         this.beginUpdate();
44045         // add children..
44046         var region = '';
44047         var abn = {};
44048         Roo.each(xitems, function(i)  {
44049             region = nb && i.region ? i.region : false;
44050             
44051             var add = ret.addxtype(i);
44052            
44053             if (region) {
44054                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
44055                 if (!i.background) {
44056                     abn[region] = nb[region] ;
44057                 }
44058             }
44059             
44060         });
44061         this.endUpdate();
44062
44063         // make the last non-background panel active..
44064         //if (nb) { Roo.log(abn); }
44065         if (nb) {
44066             
44067             for(var r in abn) {
44068                 region = this.getRegion(r);
44069                 if (region) {
44070                     // tried using nb[r], but it does not work..
44071                      
44072                     region.showPanel(abn[r]);
44073                    
44074                 }
44075             }
44076         }
44077         return ret;
44078         
44079     },
44080     
44081     
44082 // private
44083     factory : function(cfg)
44084     {
44085         
44086         var validRegions = Roo.bootstrap.layout.Border.regions;
44087
44088         var target = cfg.region;
44089         cfg.mgr = this;
44090         
44091         var r = Roo.bootstrap.layout;
44092         Roo.log(target);
44093         switch(target){
44094             case "north":
44095                 return new r.North(cfg);
44096             case "south":
44097                 return new r.South(cfg);
44098             case "east":
44099                 return new r.East(cfg);
44100             case "west":
44101                 return new r.West(cfg);
44102             case "center":
44103                 return new r.Center(cfg);
44104         }
44105         throw 'Layout region "'+target+'" not supported.';
44106     }
44107     
44108     
44109 });
44110  /*
44111  * Based on:
44112  * Ext JS Library 1.1.1
44113  * Copyright(c) 2006-2007, Ext JS, LLC.
44114  *
44115  * Originally Released Under LGPL - original licence link has changed is not relivant.
44116  *
44117  * Fork - LGPL
44118  * <script type="text/javascript">
44119  */
44120  
44121 /**
44122  * @class Roo.bootstrap.layout.Basic
44123  * @extends Roo.util.Observable
44124  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44125  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44126  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44127  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
44128  * @cfg {string}   region  the region that it inhabits..
44129  * @cfg {bool}   skipConfig skip config?
44130  * 
44131
44132  */
44133 Roo.bootstrap.layout.Basic = function(config){
44134     
44135     this.mgr = config.mgr;
44136     
44137     this.position = config.region;
44138     
44139     var skipConfig = config.skipConfig;
44140     
44141     this.events = {
44142         /**
44143          * @scope Roo.BasicLayoutRegion
44144          */
44145         
44146         /**
44147          * @event beforeremove
44148          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44149          * @param {Roo.LayoutRegion} this
44150          * @param {Roo.ContentPanel} panel The panel
44151          * @param {Object} e The cancel event object
44152          */
44153         "beforeremove" : true,
44154         /**
44155          * @event invalidated
44156          * Fires when the layout for this region is changed.
44157          * @param {Roo.LayoutRegion} this
44158          */
44159         "invalidated" : true,
44160         /**
44161          * @event visibilitychange
44162          * Fires when this region is shown or hidden 
44163          * @param {Roo.LayoutRegion} this
44164          * @param {Boolean} visibility true or false
44165          */
44166         "visibilitychange" : true,
44167         /**
44168          * @event paneladded
44169          * Fires when a panel is added. 
44170          * @param {Roo.LayoutRegion} this
44171          * @param {Roo.ContentPanel} panel The panel
44172          */
44173         "paneladded" : true,
44174         /**
44175          * @event panelremoved
44176          * Fires when a panel is removed. 
44177          * @param {Roo.LayoutRegion} this
44178          * @param {Roo.ContentPanel} panel The panel
44179          */
44180         "panelremoved" : true,
44181         /**
44182          * @event beforecollapse
44183          * Fires when this region before collapse.
44184          * @param {Roo.LayoutRegion} this
44185          */
44186         "beforecollapse" : true,
44187         /**
44188          * @event collapsed
44189          * Fires when this region is collapsed.
44190          * @param {Roo.LayoutRegion} this
44191          */
44192         "collapsed" : true,
44193         /**
44194          * @event expanded
44195          * Fires when this region is expanded.
44196          * @param {Roo.LayoutRegion} this
44197          */
44198         "expanded" : true,
44199         /**
44200          * @event slideshow
44201          * Fires when this region is slid into view.
44202          * @param {Roo.LayoutRegion} this
44203          */
44204         "slideshow" : true,
44205         /**
44206          * @event slidehide
44207          * Fires when this region slides out of view. 
44208          * @param {Roo.LayoutRegion} this
44209          */
44210         "slidehide" : true,
44211         /**
44212          * @event panelactivated
44213          * Fires when a panel is activated. 
44214          * @param {Roo.LayoutRegion} this
44215          * @param {Roo.ContentPanel} panel The activated panel
44216          */
44217         "panelactivated" : true,
44218         /**
44219          * @event resized
44220          * Fires when the user resizes this region. 
44221          * @param {Roo.LayoutRegion} this
44222          * @param {Number} newSize The new size (width for east/west, height for north/south)
44223          */
44224         "resized" : true
44225     };
44226     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44227     this.panels = new Roo.util.MixedCollection();
44228     this.panels.getKey = this.getPanelId.createDelegate(this);
44229     this.box = null;
44230     this.activePanel = null;
44231     // ensure listeners are added...
44232     
44233     if (config.listeners || config.events) {
44234         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
44235             listeners : config.listeners || {},
44236             events : config.events || {}
44237         });
44238     }
44239     
44240     if(skipConfig !== true){
44241         this.applyConfig(config);
44242     }
44243 };
44244
44245 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
44246 {
44247     getPanelId : function(p){
44248         return p.getId();
44249     },
44250     
44251     applyConfig : function(config){
44252         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44253         this.config = config;
44254         
44255     },
44256     
44257     /**
44258      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44259      * the width, for horizontal (north, south) the height.
44260      * @param {Number} newSize The new width or height
44261      */
44262     resizeTo : function(newSize){
44263         var el = this.el ? this.el :
44264                  (this.activePanel ? this.activePanel.getEl() : null);
44265         if(el){
44266             switch(this.position){
44267                 case "east":
44268                 case "west":
44269                     el.setWidth(newSize);
44270                     this.fireEvent("resized", this, newSize);
44271                 break;
44272                 case "north":
44273                 case "south":
44274                     el.setHeight(newSize);
44275                     this.fireEvent("resized", this, newSize);
44276                 break;                
44277             }
44278         }
44279     },
44280     
44281     getBox : function(){
44282         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44283     },
44284     
44285     getMargins : function(){
44286         return this.margins;
44287     },
44288     
44289     updateBox : function(box){
44290         this.box = box;
44291         var el = this.activePanel.getEl();
44292         el.dom.style.left = box.x + "px";
44293         el.dom.style.top = box.y + "px";
44294         this.activePanel.setSize(box.width, box.height);
44295     },
44296     
44297     /**
44298      * Returns the container element for this region.
44299      * @return {Roo.Element}
44300      */
44301     getEl : function(){
44302         return this.activePanel;
44303     },
44304     
44305     /**
44306      * Returns true if this region is currently visible.
44307      * @return {Boolean}
44308      */
44309     isVisible : function(){
44310         return this.activePanel ? true : false;
44311     },
44312     
44313     setActivePanel : function(panel){
44314         panel = this.getPanel(panel);
44315         if(this.activePanel && this.activePanel != panel){
44316             this.activePanel.setActiveState(false);
44317             this.activePanel.getEl().setLeftTop(-10000,-10000);
44318         }
44319         this.activePanel = panel;
44320         panel.setActiveState(true);
44321         if(this.box){
44322             panel.setSize(this.box.width, this.box.height);
44323         }
44324         this.fireEvent("panelactivated", this, panel);
44325         this.fireEvent("invalidated");
44326     },
44327     
44328     /**
44329      * Show the specified panel.
44330      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44331      * @return {Roo.ContentPanel} The shown panel or null
44332      */
44333     showPanel : function(panel){
44334         panel = this.getPanel(panel);
44335         if(panel){
44336             this.setActivePanel(panel);
44337         }
44338         return panel;
44339     },
44340     
44341     /**
44342      * Get the active panel for this region.
44343      * @return {Roo.ContentPanel} The active panel or null
44344      */
44345     getActivePanel : function(){
44346         return this.activePanel;
44347     },
44348     
44349     /**
44350      * Add the passed ContentPanel(s)
44351      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44352      * @return {Roo.ContentPanel} The panel added (if only one was added)
44353      */
44354     add : function(panel){
44355         if(arguments.length > 1){
44356             for(var i = 0, len = arguments.length; i < len; i++) {
44357                 this.add(arguments[i]);
44358             }
44359             return null;
44360         }
44361         if(this.hasPanel(panel)){
44362             this.showPanel(panel);
44363             return panel;
44364         }
44365         var el = panel.getEl();
44366         if(el.dom.parentNode != this.mgr.el.dom){
44367             this.mgr.el.dom.appendChild(el.dom);
44368         }
44369         if(panel.setRegion){
44370             panel.setRegion(this);
44371         }
44372         this.panels.add(panel);
44373         el.setStyle("position", "absolute");
44374         if(!panel.background){
44375             this.setActivePanel(panel);
44376             if(this.config.initialSize && this.panels.getCount()==1){
44377                 this.resizeTo(this.config.initialSize);
44378             }
44379         }
44380         this.fireEvent("paneladded", this, panel);
44381         return panel;
44382     },
44383     
44384     /**
44385      * Returns true if the panel is in this region.
44386      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44387      * @return {Boolean}
44388      */
44389     hasPanel : function(panel){
44390         if(typeof panel == "object"){ // must be panel obj
44391             panel = panel.getId();
44392         }
44393         return this.getPanel(panel) ? true : false;
44394     },
44395     
44396     /**
44397      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44398      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44399      * @param {Boolean} preservePanel Overrides the config preservePanel option
44400      * @return {Roo.ContentPanel} The panel that was removed
44401      */
44402     remove : function(panel, preservePanel){
44403         panel = this.getPanel(panel);
44404         if(!panel){
44405             return null;
44406         }
44407         var e = {};
44408         this.fireEvent("beforeremove", this, panel, e);
44409         if(e.cancel === true){
44410             return null;
44411         }
44412         var panelId = panel.getId();
44413         this.panels.removeKey(panelId);
44414         return panel;
44415     },
44416     
44417     /**
44418      * Returns the panel specified or null if it's not in this region.
44419      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44420      * @return {Roo.ContentPanel}
44421      */
44422     getPanel : function(id){
44423         if(typeof id == "object"){ // must be panel obj
44424             return id;
44425         }
44426         return this.panels.get(id);
44427     },
44428     
44429     /**
44430      * Returns this regions position (north/south/east/west/center).
44431      * @return {String} 
44432      */
44433     getPosition: function(){
44434         return this.position;    
44435     }
44436 });/*
44437  * Based on:
44438  * Ext JS Library 1.1.1
44439  * Copyright(c) 2006-2007, Ext JS, LLC.
44440  *
44441  * Originally Released Under LGPL - original licence link has changed is not relivant.
44442  *
44443  * Fork - LGPL
44444  * <script type="text/javascript">
44445  */
44446  
44447 /**
44448  * @class Roo.bootstrap.layout.Region
44449  * @extends Roo.bootstrap.layout.Basic
44450  * This class represents a region in a layout manager.
44451  
44452  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44453  * @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})
44454  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
44455  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
44456  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
44457  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
44458  * @cfg {String}    title           The title for the region (overrides panel titles)
44459  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
44460  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44461  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
44462  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44463  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
44464  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44465  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
44466  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
44467  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
44468  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
44469
44470  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
44471  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
44472  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
44473  * @cfg {Number}    width           For East/West panels
44474  * @cfg {Number}    height          For North/South panels
44475  * @cfg {Boolean}   split           To show the splitter
44476  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
44477  * 
44478  * @cfg {string}   cls             Extra CSS classes to add to region
44479  * 
44480  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
44481  * @cfg {string}   region  the region that it inhabits..
44482  *
44483
44484  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
44485  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
44486
44487  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
44488  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
44489  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
44490  */
44491 Roo.bootstrap.layout.Region = function(config)
44492 {
44493     this.applyConfig(config);
44494
44495     var mgr = config.mgr;
44496     var pos = config.region;
44497     config.skipConfig = true;
44498     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
44499     
44500     if (mgr.el) {
44501         this.onRender(mgr.el);   
44502     }
44503      
44504     this.visible = true;
44505     this.collapsed = false;
44506     this.unrendered_panels = [];
44507 };
44508
44509 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
44510
44511     position: '', // set by wrapper (eg. north/south etc..)
44512     unrendered_panels : null,  // unrendered panels.
44513     
44514     tabPosition : false,
44515     
44516     mgr: false, // points to 'Border'
44517     
44518     
44519     createBody : function(){
44520         /** This region's body element 
44521         * @type Roo.Element */
44522         this.bodyEl = this.el.createChild({
44523                 tag: "div",
44524                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
44525         });
44526     },
44527
44528     onRender: function(ctr, pos)
44529     {
44530         var dh = Roo.DomHelper;
44531         /** This region's container element 
44532         * @type Roo.Element */
44533         this.el = dh.append(ctr.dom, {
44534                 tag: "div",
44535                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
44536             }, true);
44537         /** This region's title element 
44538         * @type Roo.Element */
44539     
44540         this.titleEl = dh.append(this.el.dom,  {
44541                 tag: "div",
44542                 unselectable: "on",
44543                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
44544                 children:[
44545                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44546                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
44547                 ]
44548             }, true);
44549         
44550         this.titleEl.enableDisplayMode();
44551         /** This region's title text element 
44552         * @type HTMLElement */
44553         this.titleTextEl = this.titleEl.dom.firstChild;
44554         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44555         /*
44556         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
44557         this.closeBtn.enableDisplayMode();
44558         this.closeBtn.on("click", this.closeClicked, this);
44559         this.closeBtn.hide();
44560     */
44561         this.createBody(this.config);
44562         if(this.config.hideWhenEmpty){
44563             this.hide();
44564             this.on("paneladded", this.validateVisibility, this);
44565             this.on("panelremoved", this.validateVisibility, this);
44566         }
44567         if(this.autoScroll){
44568             this.bodyEl.setStyle("overflow", "auto");
44569         }else{
44570             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44571         }
44572         //if(c.titlebar !== false){
44573             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44574                 this.titleEl.hide();
44575             }else{
44576                 this.titleEl.show();
44577                 if(this.config.title){
44578                     this.titleTextEl.innerHTML = this.config.title;
44579                 }
44580             }
44581         //}
44582         if(this.config.collapsed){
44583             this.collapse(true);
44584         }
44585         if(this.config.hidden){
44586             this.hide();
44587         }
44588         
44589         if (this.unrendered_panels && this.unrendered_panels.length) {
44590             for (var i =0;i< this.unrendered_panels.length; i++) {
44591                 this.add(this.unrendered_panels[i]);
44592             }
44593             this.unrendered_panels = null;
44594             
44595         }
44596         
44597     },
44598     
44599     applyConfig : function(c)
44600     {
44601         /*
44602          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44603             var dh = Roo.DomHelper;
44604             if(c.titlebar !== false){
44605                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44606                 this.collapseBtn.on("click", this.collapse, this);
44607                 this.collapseBtn.enableDisplayMode();
44608                 /*
44609                 if(c.showPin === true || this.showPin){
44610                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44611                     this.stickBtn.enableDisplayMode();
44612                     this.stickBtn.on("click", this.expand, this);
44613                     this.stickBtn.hide();
44614                 }
44615                 
44616             }
44617             */
44618             /** This region's collapsed element
44619             * @type Roo.Element */
44620             /*
44621              *
44622             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44623                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44624             ]}, true);
44625             
44626             if(c.floatable !== false){
44627                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44628                this.collapsedEl.on("click", this.collapseClick, this);
44629             }
44630
44631             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44632                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44633                    id: "message", unselectable: "on", style:{"float":"left"}});
44634                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44635              }
44636             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44637             this.expandBtn.on("click", this.expand, this);
44638             
44639         }
44640         
44641         if(this.collapseBtn){
44642             this.collapseBtn.setVisible(c.collapsible == true);
44643         }
44644         
44645         this.cmargins = c.cmargins || this.cmargins ||
44646                          (this.position == "west" || this.position == "east" ?
44647                              {top: 0, left: 2, right:2, bottom: 0} :
44648                              {top: 2, left: 0, right:0, bottom: 2});
44649         */
44650         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44651         
44652         
44653         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44654         
44655         this.autoScroll = c.autoScroll || false;
44656         
44657         
44658        
44659         
44660         this.duration = c.duration || .30;
44661         this.slideDuration = c.slideDuration || .45;
44662         this.config = c;
44663        
44664     },
44665     /**
44666      * Returns true if this region is currently visible.
44667      * @return {Boolean}
44668      */
44669     isVisible : function(){
44670         return this.visible;
44671     },
44672
44673     /**
44674      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44675      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44676      */
44677     //setCollapsedTitle : function(title){
44678     //    title = title || "&#160;";
44679      //   if(this.collapsedTitleTextEl){
44680       //      this.collapsedTitleTextEl.innerHTML = title;
44681        // }
44682     //},
44683
44684     getBox : function(){
44685         var b;
44686       //  if(!this.collapsed){
44687             b = this.el.getBox(false, true);
44688        // }else{
44689           //  b = this.collapsedEl.getBox(false, true);
44690         //}
44691         return b;
44692     },
44693
44694     getMargins : function(){
44695         return this.margins;
44696         //return this.collapsed ? this.cmargins : this.margins;
44697     },
44698 /*
44699     highlight : function(){
44700         this.el.addClass("x-layout-panel-dragover");
44701     },
44702
44703     unhighlight : function(){
44704         this.el.removeClass("x-layout-panel-dragover");
44705     },
44706 */
44707     updateBox : function(box)
44708     {
44709         if (!this.bodyEl) {
44710             return; // not rendered yet..
44711         }
44712         
44713         this.box = box;
44714         if(!this.collapsed){
44715             this.el.dom.style.left = box.x + "px";
44716             this.el.dom.style.top = box.y + "px";
44717             this.updateBody(box.width, box.height);
44718         }else{
44719             this.collapsedEl.dom.style.left = box.x + "px";
44720             this.collapsedEl.dom.style.top = box.y + "px";
44721             this.collapsedEl.setSize(box.width, box.height);
44722         }
44723         if(this.tabs){
44724             this.tabs.autoSizeTabs();
44725         }
44726     },
44727
44728     updateBody : function(w, h)
44729     {
44730         if(w !== null){
44731             this.el.setWidth(w);
44732             w -= this.el.getBorderWidth("rl");
44733             if(this.config.adjustments){
44734                 w += this.config.adjustments[0];
44735             }
44736         }
44737         if(h !== null && h > 0){
44738             this.el.setHeight(h);
44739             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44740             h -= this.el.getBorderWidth("tb");
44741             if(this.config.adjustments){
44742                 h += this.config.adjustments[1];
44743             }
44744             this.bodyEl.setHeight(h);
44745             if(this.tabs){
44746                 h = this.tabs.syncHeight(h);
44747             }
44748         }
44749         if(this.panelSize){
44750             w = w !== null ? w : this.panelSize.width;
44751             h = h !== null ? h : this.panelSize.height;
44752         }
44753         if(this.activePanel){
44754             var el = this.activePanel.getEl();
44755             w = w !== null ? w : el.getWidth();
44756             h = h !== null ? h : el.getHeight();
44757             this.panelSize = {width: w, height: h};
44758             this.activePanel.setSize(w, h);
44759         }
44760         if(Roo.isIE && this.tabs){
44761             this.tabs.el.repaint();
44762         }
44763     },
44764
44765     /**
44766      * Returns the container element for this region.
44767      * @return {Roo.Element}
44768      */
44769     getEl : function(){
44770         return this.el;
44771     },
44772
44773     /**
44774      * Hides this region.
44775      */
44776     hide : function(){
44777         //if(!this.collapsed){
44778             this.el.dom.style.left = "-2000px";
44779             this.el.hide();
44780         //}else{
44781          //   this.collapsedEl.dom.style.left = "-2000px";
44782          //   this.collapsedEl.hide();
44783        // }
44784         this.visible = false;
44785         this.fireEvent("visibilitychange", this, false);
44786     },
44787
44788     /**
44789      * Shows this region if it was previously hidden.
44790      */
44791     show : function(){
44792         //if(!this.collapsed){
44793             this.el.show();
44794         //}else{
44795         //    this.collapsedEl.show();
44796        // }
44797         this.visible = true;
44798         this.fireEvent("visibilitychange", this, true);
44799     },
44800 /*
44801     closeClicked : function(){
44802         if(this.activePanel){
44803             this.remove(this.activePanel);
44804         }
44805     },
44806
44807     collapseClick : function(e){
44808         if(this.isSlid){
44809            e.stopPropagation();
44810            this.slideIn();
44811         }else{
44812            e.stopPropagation();
44813            this.slideOut();
44814         }
44815     },
44816 */
44817     /**
44818      * Collapses this region.
44819      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44820      */
44821     /*
44822     collapse : function(skipAnim, skipCheck = false){
44823         if(this.collapsed) {
44824             return;
44825         }
44826         
44827         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44828             
44829             this.collapsed = true;
44830             if(this.split){
44831                 this.split.el.hide();
44832             }
44833             if(this.config.animate && skipAnim !== true){
44834                 this.fireEvent("invalidated", this);
44835                 this.animateCollapse();
44836             }else{
44837                 this.el.setLocation(-20000,-20000);
44838                 this.el.hide();
44839                 this.collapsedEl.show();
44840                 this.fireEvent("collapsed", this);
44841                 this.fireEvent("invalidated", this);
44842             }
44843         }
44844         
44845     },
44846 */
44847     animateCollapse : function(){
44848         // overridden
44849     },
44850
44851     /**
44852      * Expands this region if it was previously collapsed.
44853      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44854      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44855      */
44856     /*
44857     expand : function(e, skipAnim){
44858         if(e) {
44859             e.stopPropagation();
44860         }
44861         if(!this.collapsed || this.el.hasActiveFx()) {
44862             return;
44863         }
44864         if(this.isSlid){
44865             this.afterSlideIn();
44866             skipAnim = true;
44867         }
44868         this.collapsed = false;
44869         if(this.config.animate && skipAnim !== true){
44870             this.animateExpand();
44871         }else{
44872             this.el.show();
44873             if(this.split){
44874                 this.split.el.show();
44875             }
44876             this.collapsedEl.setLocation(-2000,-2000);
44877             this.collapsedEl.hide();
44878             this.fireEvent("invalidated", this);
44879             this.fireEvent("expanded", this);
44880         }
44881     },
44882 */
44883     animateExpand : function(){
44884         // overridden
44885     },
44886
44887     initTabs : function()
44888     {
44889         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44890         
44891         var ts = new Roo.bootstrap.panel.Tabs({
44892             el: this.bodyEl.dom,
44893             region : this,
44894             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
44895             disableTooltips: this.config.disableTabTips,
44896             toolbar : this.config.toolbar
44897         });
44898         
44899         if(this.config.hideTabs){
44900             ts.stripWrap.setDisplayed(false);
44901         }
44902         this.tabs = ts;
44903         ts.resizeTabs = this.config.resizeTabs === true;
44904         ts.minTabWidth = this.config.minTabWidth || 40;
44905         ts.maxTabWidth = this.config.maxTabWidth || 250;
44906         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44907         ts.monitorResize = false;
44908         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44909         ts.bodyEl.addClass('roo-layout-tabs-body');
44910         this.panels.each(this.initPanelAsTab, this);
44911     },
44912
44913     initPanelAsTab : function(panel){
44914         var ti = this.tabs.addTab(
44915             panel.getEl().id,
44916             panel.getTitle(),
44917             null,
44918             this.config.closeOnTab && panel.isClosable(),
44919             panel.tpl
44920         );
44921         if(panel.tabTip !== undefined){
44922             ti.setTooltip(panel.tabTip);
44923         }
44924         ti.on("activate", function(){
44925               this.setActivePanel(panel);
44926         }, this);
44927         
44928         if(this.config.closeOnTab){
44929             ti.on("beforeclose", function(t, e){
44930                 e.cancel = true;
44931                 this.remove(panel);
44932             }, this);
44933         }
44934         
44935         panel.tabItem = ti;
44936         
44937         return ti;
44938     },
44939
44940     updatePanelTitle : function(panel, title)
44941     {
44942         if(this.activePanel == panel){
44943             this.updateTitle(title);
44944         }
44945         if(this.tabs){
44946             var ti = this.tabs.getTab(panel.getEl().id);
44947             ti.setText(title);
44948             if(panel.tabTip !== undefined){
44949                 ti.setTooltip(panel.tabTip);
44950             }
44951         }
44952     },
44953
44954     updateTitle : function(title){
44955         if(this.titleTextEl && !this.config.title){
44956             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44957         }
44958     },
44959
44960     setActivePanel : function(panel)
44961     {
44962         panel = this.getPanel(panel);
44963         if(this.activePanel && this.activePanel != panel){
44964             if(this.activePanel.setActiveState(false) === false){
44965                 return;
44966             }
44967         }
44968         this.activePanel = panel;
44969         panel.setActiveState(true);
44970         if(this.panelSize){
44971             panel.setSize(this.panelSize.width, this.panelSize.height);
44972         }
44973         if(this.closeBtn){
44974             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44975         }
44976         this.updateTitle(panel.getTitle());
44977         if(this.tabs){
44978             this.fireEvent("invalidated", this);
44979         }
44980         this.fireEvent("panelactivated", this, panel);
44981     },
44982
44983     /**
44984      * Shows the specified panel.
44985      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44986      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44987      */
44988     showPanel : function(panel)
44989     {
44990         panel = this.getPanel(panel);
44991         if(panel){
44992             if(this.tabs){
44993                 var tab = this.tabs.getTab(panel.getEl().id);
44994                 if(tab.isHidden()){
44995                     this.tabs.unhideTab(tab.id);
44996                 }
44997                 tab.activate();
44998             }else{
44999                 this.setActivePanel(panel);
45000             }
45001         }
45002         return panel;
45003     },
45004
45005     /**
45006      * Get the active panel for this region.
45007      * @return {Roo.ContentPanel} The active panel or null
45008      */
45009     getActivePanel : function(){
45010         return this.activePanel;
45011     },
45012
45013     validateVisibility : function(){
45014         if(this.panels.getCount() < 1){
45015             this.updateTitle("&#160;");
45016             this.closeBtn.hide();
45017             this.hide();
45018         }else{
45019             if(!this.isVisible()){
45020                 this.show();
45021             }
45022         }
45023     },
45024
45025     /**
45026      * Adds the passed ContentPanel(s) to this region.
45027      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45028      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45029      */
45030     add : function(panel)
45031     {
45032         if(arguments.length > 1){
45033             for(var i = 0, len = arguments.length; i < len; i++) {
45034                 this.add(arguments[i]);
45035             }
45036             return null;
45037         }
45038         
45039         // if we have not been rendered yet, then we can not really do much of this..
45040         if (!this.bodyEl) {
45041             this.unrendered_panels.push(panel);
45042             return panel;
45043         }
45044         
45045         
45046         
45047         
45048         if(this.hasPanel(panel)){
45049             this.showPanel(panel);
45050             return panel;
45051         }
45052         panel.setRegion(this);
45053         this.panels.add(panel);
45054        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45055             // sinle panel - no tab...?? would it not be better to render it with the tabs,
45056             // and hide them... ???
45057             this.bodyEl.dom.appendChild(panel.getEl().dom);
45058             if(panel.background !== true){
45059                 this.setActivePanel(panel);
45060             }
45061             this.fireEvent("paneladded", this, panel);
45062             return panel;
45063         }
45064         */
45065         if(!this.tabs){
45066             this.initTabs();
45067         }else{
45068             this.initPanelAsTab(panel);
45069         }
45070         
45071         
45072         if(panel.background !== true){
45073             this.tabs.activate(panel.getEl().id);
45074         }
45075         this.fireEvent("paneladded", this, panel);
45076         return panel;
45077     },
45078
45079     /**
45080      * Hides the tab for the specified panel.
45081      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45082      */
45083     hidePanel : function(panel){
45084         if(this.tabs && (panel = this.getPanel(panel))){
45085             this.tabs.hideTab(panel.getEl().id);
45086         }
45087     },
45088
45089     /**
45090      * Unhides the tab for a previously hidden panel.
45091      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45092      */
45093     unhidePanel : function(panel){
45094         if(this.tabs && (panel = this.getPanel(panel))){
45095             this.tabs.unhideTab(panel.getEl().id);
45096         }
45097     },
45098
45099     clearPanels : function(){
45100         while(this.panels.getCount() > 0){
45101              this.remove(this.panels.first());
45102         }
45103     },
45104
45105     /**
45106      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45107      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45108      * @param {Boolean} preservePanel Overrides the config preservePanel option
45109      * @return {Roo.ContentPanel} The panel that was removed
45110      */
45111     remove : function(panel, preservePanel)
45112     {
45113         panel = this.getPanel(panel);
45114         if(!panel){
45115             return null;
45116         }
45117         var e = {};
45118         this.fireEvent("beforeremove", this, panel, e);
45119         if(e.cancel === true){
45120             return null;
45121         }
45122         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45123         var panelId = panel.getId();
45124         this.panels.removeKey(panelId);
45125         if(preservePanel){
45126             document.body.appendChild(panel.getEl().dom);
45127         }
45128         if(this.tabs){
45129             this.tabs.removeTab(panel.getEl().id);
45130         }else if (!preservePanel){
45131             this.bodyEl.dom.removeChild(panel.getEl().dom);
45132         }
45133         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45134             var p = this.panels.first();
45135             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45136             tempEl.appendChild(p.getEl().dom);
45137             this.bodyEl.update("");
45138             this.bodyEl.dom.appendChild(p.getEl().dom);
45139             tempEl = null;
45140             this.updateTitle(p.getTitle());
45141             this.tabs = null;
45142             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45143             this.setActivePanel(p);
45144         }
45145         panel.setRegion(null);
45146         if(this.activePanel == panel){
45147             this.activePanel = null;
45148         }
45149         if(this.config.autoDestroy !== false && preservePanel !== true){
45150             try{panel.destroy();}catch(e){}
45151         }
45152         this.fireEvent("panelremoved", this, panel);
45153         return panel;
45154     },
45155
45156     /**
45157      * Returns the TabPanel component used by this region
45158      * @return {Roo.TabPanel}
45159      */
45160     getTabs : function(){
45161         return this.tabs;
45162     },
45163
45164     createTool : function(parentEl, className){
45165         var btn = Roo.DomHelper.append(parentEl, {
45166             tag: "div",
45167             cls: "x-layout-tools-button",
45168             children: [ {
45169                 tag: "div",
45170                 cls: "roo-layout-tools-button-inner " + className,
45171                 html: "&#160;"
45172             }]
45173         }, true);
45174         btn.addClassOnOver("roo-layout-tools-button-over");
45175         return btn;
45176     }
45177 });/*
45178  * Based on:
45179  * Ext JS Library 1.1.1
45180  * Copyright(c) 2006-2007, Ext JS, LLC.
45181  *
45182  * Originally Released Under LGPL - original licence link has changed is not relivant.
45183  *
45184  * Fork - LGPL
45185  * <script type="text/javascript">
45186  */
45187  
45188
45189
45190 /**
45191  * @class Roo.SplitLayoutRegion
45192  * @extends Roo.LayoutRegion
45193  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45194  */
45195 Roo.bootstrap.layout.Split = function(config){
45196     this.cursor = config.cursor;
45197     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
45198 };
45199
45200 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
45201 {
45202     splitTip : "Drag to resize.",
45203     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45204     useSplitTips : false,
45205
45206     applyConfig : function(config){
45207         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
45208     },
45209     
45210     onRender : function(ctr,pos) {
45211         
45212         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
45213         if(!this.config.split){
45214             return;
45215         }
45216         if(!this.split){
45217             
45218             var splitEl = Roo.DomHelper.append(ctr.dom,  {
45219                             tag: "div",
45220                             id: this.el.id + "-split",
45221                             cls: "roo-layout-split roo-layout-split-"+this.position,
45222                             html: "&#160;"
45223             });
45224             /** The SplitBar for this region 
45225             * @type Roo.SplitBar */
45226             // does not exist yet...
45227             Roo.log([this.position, this.orientation]);
45228             
45229             this.split = new Roo.bootstrap.SplitBar({
45230                 dragElement : splitEl,
45231                 resizingElement: this.el,
45232                 orientation : this.orientation
45233             });
45234             
45235             this.split.on("moved", this.onSplitMove, this);
45236             this.split.useShim = this.config.useShim === true;
45237             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45238             if(this.useSplitTips){
45239                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45240             }
45241             //if(config.collapsible){
45242             //    this.split.el.on("dblclick", this.collapse,  this);
45243             //}
45244         }
45245         if(typeof this.config.minSize != "undefined"){
45246             this.split.minSize = this.config.minSize;
45247         }
45248         if(typeof this.config.maxSize != "undefined"){
45249             this.split.maxSize = this.config.maxSize;
45250         }
45251         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
45252             this.hideSplitter();
45253         }
45254         
45255     },
45256
45257     getHMaxSize : function(){
45258          var cmax = this.config.maxSize || 10000;
45259          var center = this.mgr.getRegion("center");
45260          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45261     },
45262
45263     getVMaxSize : function(){
45264          var cmax = this.config.maxSize || 10000;
45265          var center = this.mgr.getRegion("center");
45266          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45267     },
45268
45269     onSplitMove : function(split, newSize){
45270         this.fireEvent("resized", this, newSize);
45271     },
45272     
45273     /** 
45274      * Returns the {@link Roo.SplitBar} for this region.
45275      * @return {Roo.SplitBar}
45276      */
45277     getSplitBar : function(){
45278         return this.split;
45279     },
45280     
45281     hide : function(){
45282         this.hideSplitter();
45283         Roo.bootstrap.layout.Split.superclass.hide.call(this);
45284     },
45285
45286     hideSplitter : function(){
45287         if(this.split){
45288             this.split.el.setLocation(-2000,-2000);
45289             this.split.el.hide();
45290         }
45291     },
45292
45293     show : function(){
45294         if(this.split){
45295             this.split.el.show();
45296         }
45297         Roo.bootstrap.layout.Split.superclass.show.call(this);
45298     },
45299     
45300     beforeSlide: function(){
45301         if(Roo.isGecko){// firefox overflow auto bug workaround
45302             this.bodyEl.clip();
45303             if(this.tabs) {
45304                 this.tabs.bodyEl.clip();
45305             }
45306             if(this.activePanel){
45307                 this.activePanel.getEl().clip();
45308                 
45309                 if(this.activePanel.beforeSlide){
45310                     this.activePanel.beforeSlide();
45311                 }
45312             }
45313         }
45314     },
45315     
45316     afterSlide : function(){
45317         if(Roo.isGecko){// firefox overflow auto bug workaround
45318             this.bodyEl.unclip();
45319             if(this.tabs) {
45320                 this.tabs.bodyEl.unclip();
45321             }
45322             if(this.activePanel){
45323                 this.activePanel.getEl().unclip();
45324                 if(this.activePanel.afterSlide){
45325                     this.activePanel.afterSlide();
45326                 }
45327             }
45328         }
45329     },
45330
45331     initAutoHide : function(){
45332         if(this.autoHide !== false){
45333             if(!this.autoHideHd){
45334                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45335                 this.autoHideHd = {
45336                     "mouseout": function(e){
45337                         if(!e.within(this.el, true)){
45338                             st.delay(500);
45339                         }
45340                     },
45341                     "mouseover" : function(e){
45342                         st.cancel();
45343                     },
45344                     scope : this
45345                 };
45346             }
45347             this.el.on(this.autoHideHd);
45348         }
45349     },
45350
45351     clearAutoHide : function(){
45352         if(this.autoHide !== false){
45353             this.el.un("mouseout", this.autoHideHd.mouseout);
45354             this.el.un("mouseover", this.autoHideHd.mouseover);
45355         }
45356     },
45357
45358     clearMonitor : function(){
45359         Roo.get(document).un("click", this.slideInIf, this);
45360     },
45361
45362     // these names are backwards but not changed for compat
45363     slideOut : function(){
45364         if(this.isSlid || this.el.hasActiveFx()){
45365             return;
45366         }
45367         this.isSlid = true;
45368         if(this.collapseBtn){
45369             this.collapseBtn.hide();
45370         }
45371         this.closeBtnState = this.closeBtn.getStyle('display');
45372         this.closeBtn.hide();
45373         if(this.stickBtn){
45374             this.stickBtn.show();
45375         }
45376         this.el.show();
45377         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45378         this.beforeSlide();
45379         this.el.setStyle("z-index", 10001);
45380         this.el.slideIn(this.getSlideAnchor(), {
45381             callback: function(){
45382                 this.afterSlide();
45383                 this.initAutoHide();
45384                 Roo.get(document).on("click", this.slideInIf, this);
45385                 this.fireEvent("slideshow", this);
45386             },
45387             scope: this,
45388             block: true
45389         });
45390     },
45391
45392     afterSlideIn : function(){
45393         this.clearAutoHide();
45394         this.isSlid = false;
45395         this.clearMonitor();
45396         this.el.setStyle("z-index", "");
45397         if(this.collapseBtn){
45398             this.collapseBtn.show();
45399         }
45400         this.closeBtn.setStyle('display', this.closeBtnState);
45401         if(this.stickBtn){
45402             this.stickBtn.hide();
45403         }
45404         this.fireEvent("slidehide", this);
45405     },
45406
45407     slideIn : function(cb){
45408         if(!this.isSlid || this.el.hasActiveFx()){
45409             Roo.callback(cb);
45410             return;
45411         }
45412         this.isSlid = false;
45413         this.beforeSlide();
45414         this.el.slideOut(this.getSlideAnchor(), {
45415             callback: function(){
45416                 this.el.setLeftTop(-10000, -10000);
45417                 this.afterSlide();
45418                 this.afterSlideIn();
45419                 Roo.callback(cb);
45420             },
45421             scope: this,
45422             block: true
45423         });
45424     },
45425     
45426     slideInIf : function(e){
45427         if(!e.within(this.el)){
45428             this.slideIn();
45429         }
45430     },
45431
45432     animateCollapse : function(){
45433         this.beforeSlide();
45434         this.el.setStyle("z-index", 20000);
45435         var anchor = this.getSlideAnchor();
45436         this.el.slideOut(anchor, {
45437             callback : function(){
45438                 this.el.setStyle("z-index", "");
45439                 this.collapsedEl.slideIn(anchor, {duration:.3});
45440                 this.afterSlide();
45441                 this.el.setLocation(-10000,-10000);
45442                 this.el.hide();
45443                 this.fireEvent("collapsed", this);
45444             },
45445             scope: this,
45446             block: true
45447         });
45448     },
45449
45450     animateExpand : function(){
45451         this.beforeSlide();
45452         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45453         this.el.setStyle("z-index", 20000);
45454         this.collapsedEl.hide({
45455             duration:.1
45456         });
45457         this.el.slideIn(this.getSlideAnchor(), {
45458             callback : function(){
45459                 this.el.setStyle("z-index", "");
45460                 this.afterSlide();
45461                 if(this.split){
45462                     this.split.el.show();
45463                 }
45464                 this.fireEvent("invalidated", this);
45465                 this.fireEvent("expanded", this);
45466             },
45467             scope: this,
45468             block: true
45469         });
45470     },
45471
45472     anchors : {
45473         "west" : "left",
45474         "east" : "right",
45475         "north" : "top",
45476         "south" : "bottom"
45477     },
45478
45479     sanchors : {
45480         "west" : "l",
45481         "east" : "r",
45482         "north" : "t",
45483         "south" : "b"
45484     },
45485
45486     canchors : {
45487         "west" : "tl-tr",
45488         "east" : "tr-tl",
45489         "north" : "tl-bl",
45490         "south" : "bl-tl"
45491     },
45492
45493     getAnchor : function(){
45494         return this.anchors[this.position];
45495     },
45496
45497     getCollapseAnchor : function(){
45498         return this.canchors[this.position];
45499     },
45500
45501     getSlideAnchor : function(){
45502         return this.sanchors[this.position];
45503     },
45504
45505     getAlignAdj : function(){
45506         var cm = this.cmargins;
45507         switch(this.position){
45508             case "west":
45509                 return [0, 0];
45510             break;
45511             case "east":
45512                 return [0, 0];
45513             break;
45514             case "north":
45515                 return [0, 0];
45516             break;
45517             case "south":
45518                 return [0, 0];
45519             break;
45520         }
45521     },
45522
45523     getExpandAdj : function(){
45524         var c = this.collapsedEl, cm = this.cmargins;
45525         switch(this.position){
45526             case "west":
45527                 return [-(cm.right+c.getWidth()+cm.left), 0];
45528             break;
45529             case "east":
45530                 return [cm.right+c.getWidth()+cm.left, 0];
45531             break;
45532             case "north":
45533                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45534             break;
45535             case "south":
45536                 return [0, cm.top+cm.bottom+c.getHeight()];
45537             break;
45538         }
45539     }
45540 });/*
45541  * Based on:
45542  * Ext JS Library 1.1.1
45543  * Copyright(c) 2006-2007, Ext JS, LLC.
45544  *
45545  * Originally Released Under LGPL - original licence link has changed is not relivant.
45546  *
45547  * Fork - LGPL
45548  * <script type="text/javascript">
45549  */
45550 /*
45551  * These classes are private internal classes
45552  */
45553 Roo.bootstrap.layout.Center = function(config){
45554     config.region = "center";
45555     Roo.bootstrap.layout.Region.call(this, config);
45556     this.visible = true;
45557     this.minWidth = config.minWidth || 20;
45558     this.minHeight = config.minHeight || 20;
45559 };
45560
45561 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
45562     hide : function(){
45563         // center panel can't be hidden
45564     },
45565     
45566     show : function(){
45567         // center panel can't be hidden
45568     },
45569     
45570     getMinWidth: function(){
45571         return this.minWidth;
45572     },
45573     
45574     getMinHeight: function(){
45575         return this.minHeight;
45576     }
45577 });
45578
45579
45580
45581
45582  
45583
45584
45585
45586
45587
45588
45589 Roo.bootstrap.layout.North = function(config)
45590 {
45591     config.region = 'north';
45592     config.cursor = 'n-resize';
45593     
45594     Roo.bootstrap.layout.Split.call(this, config);
45595     
45596     
45597     if(this.split){
45598         this.split.placement = Roo.bootstrap.SplitBar.TOP;
45599         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45600         this.split.el.addClass("roo-layout-split-v");
45601     }
45602     //var size = config.initialSize || config.height;
45603     //if(this.el && typeof size != "undefined"){
45604     //    this.el.setHeight(size);
45605     //}
45606 };
45607 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45608 {
45609     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45610      
45611      
45612     onRender : function(ctr, pos)
45613     {
45614         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45615         var size = this.config.initialSize || this.config.height;
45616         if(this.el && typeof size != "undefined"){
45617             this.el.setHeight(size);
45618         }
45619     
45620     },
45621     
45622     getBox : function(){
45623         if(this.collapsed){
45624             return this.collapsedEl.getBox();
45625         }
45626         var box = this.el.getBox();
45627         if(this.split){
45628             box.height += this.split.el.getHeight();
45629         }
45630         return box;
45631     },
45632     
45633     updateBox : function(box){
45634         if(this.split && !this.collapsed){
45635             box.height -= this.split.el.getHeight();
45636             this.split.el.setLeft(box.x);
45637             this.split.el.setTop(box.y+box.height);
45638             this.split.el.setWidth(box.width);
45639         }
45640         if(this.collapsed){
45641             this.updateBody(box.width, null);
45642         }
45643         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45644     }
45645 });
45646
45647
45648
45649
45650
45651 Roo.bootstrap.layout.South = function(config){
45652     config.region = 'south';
45653     config.cursor = 's-resize';
45654     Roo.bootstrap.layout.Split.call(this, config);
45655     if(this.split){
45656         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45657         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45658         this.split.el.addClass("roo-layout-split-v");
45659     }
45660     
45661 };
45662
45663 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45664     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45665     
45666     onRender : function(ctr, pos)
45667     {
45668         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45669         var size = this.config.initialSize || this.config.height;
45670         if(this.el && typeof size != "undefined"){
45671             this.el.setHeight(size);
45672         }
45673     
45674     },
45675     
45676     getBox : function(){
45677         if(this.collapsed){
45678             return this.collapsedEl.getBox();
45679         }
45680         var box = this.el.getBox();
45681         if(this.split){
45682             var sh = this.split.el.getHeight();
45683             box.height += sh;
45684             box.y -= sh;
45685         }
45686         return box;
45687     },
45688     
45689     updateBox : function(box){
45690         if(this.split && !this.collapsed){
45691             var sh = this.split.el.getHeight();
45692             box.height -= sh;
45693             box.y += sh;
45694             this.split.el.setLeft(box.x);
45695             this.split.el.setTop(box.y-sh);
45696             this.split.el.setWidth(box.width);
45697         }
45698         if(this.collapsed){
45699             this.updateBody(box.width, null);
45700         }
45701         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45702     }
45703 });
45704
45705 Roo.bootstrap.layout.East = function(config){
45706     config.region = "east";
45707     config.cursor = "e-resize";
45708     Roo.bootstrap.layout.Split.call(this, config);
45709     if(this.split){
45710         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45711         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45712         this.split.el.addClass("roo-layout-split-h");
45713     }
45714     
45715 };
45716 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45717     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45718     
45719     onRender : function(ctr, pos)
45720     {
45721         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45722         var size = this.config.initialSize || this.config.width;
45723         if(this.el && typeof size != "undefined"){
45724             this.el.setWidth(size);
45725         }
45726     
45727     },
45728     
45729     getBox : function(){
45730         if(this.collapsed){
45731             return this.collapsedEl.getBox();
45732         }
45733         var box = this.el.getBox();
45734         if(this.split){
45735             var sw = this.split.el.getWidth();
45736             box.width += sw;
45737             box.x -= sw;
45738         }
45739         return box;
45740     },
45741
45742     updateBox : function(box){
45743         if(this.split && !this.collapsed){
45744             var sw = this.split.el.getWidth();
45745             box.width -= sw;
45746             this.split.el.setLeft(box.x);
45747             this.split.el.setTop(box.y);
45748             this.split.el.setHeight(box.height);
45749             box.x += sw;
45750         }
45751         if(this.collapsed){
45752             this.updateBody(null, box.height);
45753         }
45754         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45755     }
45756 });
45757
45758 Roo.bootstrap.layout.West = function(config){
45759     config.region = "west";
45760     config.cursor = "w-resize";
45761     
45762     Roo.bootstrap.layout.Split.call(this, config);
45763     if(this.split){
45764         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45765         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45766         this.split.el.addClass("roo-layout-split-h");
45767     }
45768     
45769 };
45770 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45771     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45772     
45773     onRender: function(ctr, pos)
45774     {
45775         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45776         var size = this.config.initialSize || this.config.width;
45777         if(typeof size != "undefined"){
45778             this.el.setWidth(size);
45779         }
45780     },
45781     
45782     getBox : function(){
45783         if(this.collapsed){
45784             return this.collapsedEl.getBox();
45785         }
45786         var box = this.el.getBox();
45787         if (box.width == 0) {
45788             box.width = this.config.width; // kludge?
45789         }
45790         if(this.split){
45791             box.width += this.split.el.getWidth();
45792         }
45793         return box;
45794     },
45795     
45796     updateBox : function(box){
45797         if(this.split && !this.collapsed){
45798             var sw = this.split.el.getWidth();
45799             box.width -= sw;
45800             this.split.el.setLeft(box.x+box.width);
45801             this.split.el.setTop(box.y);
45802             this.split.el.setHeight(box.height);
45803         }
45804         if(this.collapsed){
45805             this.updateBody(null, box.height);
45806         }
45807         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45808     }
45809 });/*
45810  * Based on:
45811  * Ext JS Library 1.1.1
45812  * Copyright(c) 2006-2007, Ext JS, LLC.
45813  *
45814  * Originally Released Under LGPL - original licence link has changed is not relivant.
45815  *
45816  * Fork - LGPL
45817  * <script type="text/javascript">
45818  */
45819 /**
45820  * @class Roo.bootstrap.paenl.Content
45821  * @extends Roo.util.Observable
45822  * @children Roo.bootstrap.Component
45823  * @parent builder Roo.bootstrap.layout.Border
45824  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45825  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45826  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45827  * @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
45828  * @cfg {Boolean}   closable      True if the panel can be closed/removed
45829  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
45830  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45831  * @cfg {Toolbar}   toolbar       A toolbar for this panel
45832  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
45833  * @cfg {String} title          The title for this panel
45834  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45835  * @cfg {String} url            Calls {@link #setUrl} with this value
45836  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45837  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
45838  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
45839  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
45840  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
45841  * @cfg {Boolean} badges render the badges
45842  * @cfg {String} cls  extra classes to use  
45843  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45844  
45845  * @constructor
45846  * Create a new ContentPanel.
45847  * @param {String/Object} config A string to set only the title or a config object
45848  
45849  */
45850 Roo.bootstrap.panel.Content = function( config){
45851     
45852     this.tpl = config.tpl || false;
45853     
45854     var el = config.el;
45855     var content = config.content;
45856
45857     if(config.autoCreate){ // xtype is available if this is called from factory
45858         el = Roo.id();
45859     }
45860     this.el = Roo.get(el);
45861     if(!this.el && config && config.autoCreate){
45862         if(typeof config.autoCreate == "object"){
45863             if(!config.autoCreate.id){
45864                 config.autoCreate.id = config.id||el;
45865             }
45866             this.el = Roo.DomHelper.append(document.body,
45867                         config.autoCreate, true);
45868         }else{
45869             var elcfg =  {
45870                 tag: "div",
45871                 cls: (config.cls || '') +
45872                     (config.background ? ' bg-' + config.background : '') +
45873                     " roo-layout-inactive-content",
45874                 id: config.id||el
45875             };
45876             if (config.iframe) {
45877                 elcfg.cn = [
45878                     {
45879                         tag : 'iframe',
45880                         style : 'border: 0px',
45881                         src : 'data:text/html,%3Cbody%3E%3C%2Fbody%3E'
45882                     }
45883                 ];
45884             }
45885               
45886             if (config.html) {
45887                 elcfg.html = config.html;
45888                 
45889             }
45890                         
45891             this.el = Roo.DomHelper.append(document.body, elcfg , true);
45892             if (config.iframe) {
45893                 this.iframeEl = this.el.select('iframe',true).first();
45894             }
45895             
45896         }
45897     } 
45898     this.closable = false;
45899     this.loaded = false;
45900     this.active = false;
45901    
45902       
45903     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45904         
45905         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45906         
45907         this.wrapEl = this.el; //this.el.wrap();
45908         var ti = [];
45909         if (config.toolbar.items) {
45910             ti = config.toolbar.items ;
45911             delete config.toolbar.items ;
45912         }
45913         
45914         var nitems = [];
45915         this.toolbar.render(this.wrapEl, 'before');
45916         for(var i =0;i < ti.length;i++) {
45917           //  Roo.log(['add child', items[i]]);
45918             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45919         }
45920         this.toolbar.items = nitems;
45921         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45922         delete config.toolbar;
45923         
45924     }
45925     /*
45926     // xtype created footer. - not sure if will work as we normally have to render first..
45927     if (this.footer && !this.footer.el && this.footer.xtype) {
45928         if (!this.wrapEl) {
45929             this.wrapEl = this.el.wrap();
45930         }
45931     
45932         this.footer.container = this.wrapEl.createChild();
45933          
45934         this.footer = Roo.factory(this.footer, Roo);
45935         
45936     }
45937     */
45938     
45939      if(typeof config == "string"){
45940         this.title = config;
45941     }else{
45942         Roo.apply(this, config);
45943     }
45944     
45945     if(this.resizeEl){
45946         this.resizeEl = Roo.get(this.resizeEl, true);
45947     }else{
45948         this.resizeEl = this.el;
45949     }
45950     // handle view.xtype
45951     
45952  
45953     
45954     
45955     this.addEvents({
45956         /**
45957          * @event activate
45958          * Fires when this panel is activated. 
45959          * @param {Roo.ContentPanel} this
45960          */
45961         "activate" : true,
45962         /**
45963          * @event deactivate
45964          * Fires when this panel is activated. 
45965          * @param {Roo.ContentPanel} this
45966          */
45967         "deactivate" : true,
45968
45969         /**
45970          * @event resize
45971          * Fires when this panel is resized if fitToFrame is true.
45972          * @param {Roo.ContentPanel} this
45973          * @param {Number} width The width after any component adjustments
45974          * @param {Number} height The height after any component adjustments
45975          */
45976         "resize" : true,
45977         
45978          /**
45979          * @event render
45980          * Fires when this tab is created
45981          * @param {Roo.ContentPanel} this
45982          */
45983         "render" : true,
45984         
45985           /**
45986          * @event scroll
45987          * Fires when this content is scrolled
45988          * @param {Roo.ContentPanel} this
45989          * @param {Event} scrollEvent
45990          */
45991         "scroll" : true
45992         
45993         
45994         
45995     });
45996     
45997
45998     
45999     
46000     if(this.autoScroll && !this.iframe){
46001         this.resizeEl.setStyle("overflow", "auto");
46002         this.resizeEl.on('scroll', this.onScroll, this);
46003     } else {
46004         // fix randome scrolling
46005         //this.el.on('scroll', function() {
46006         //    Roo.log('fix random scolling');
46007         //    this.scrollTo('top',0); 
46008         //});
46009     }
46010     content = content || this.content;
46011     if(content){
46012         this.setContent(content);
46013     }
46014     if(config && config.url){
46015         this.setUrl(this.url, this.params, this.loadOnce);
46016     }
46017     
46018     
46019     
46020     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
46021     
46022     if (this.view && typeof(this.view.xtype) != 'undefined') {
46023         this.view.el = this.el.appendChild(document.createElement("div"));
46024         this.view = Roo.factory(this.view); 
46025         this.view.render  &&  this.view.render(false, '');  
46026     }
46027     
46028     
46029     this.fireEvent('render', this);
46030 };
46031
46032 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
46033     
46034     cls : '',
46035     background : '',
46036     
46037     tabTip : '',
46038     
46039     iframe : false,
46040     iframeEl : false,
46041     
46042     /* Resize Element - use this to work out scroll etc. */
46043     resizeEl : false,
46044     
46045     setRegion : function(region){
46046         this.region = region;
46047         this.setActiveClass(region && !this.background);
46048     },
46049     
46050     
46051     setActiveClass: function(state)
46052     {
46053         if(state){
46054            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
46055            this.el.setStyle('position','relative');
46056         }else{
46057            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
46058            this.el.setStyle('position', 'absolute');
46059         } 
46060     },
46061     
46062     /**
46063      * Returns the toolbar for this Panel if one was configured. 
46064      * @return {Roo.Toolbar} 
46065      */
46066     getToolbar : function(){
46067         return this.toolbar;
46068     },
46069     
46070     setActiveState : function(active)
46071     {
46072         this.active = active;
46073         this.setActiveClass(active);
46074         if(!active){
46075             if(this.fireEvent("deactivate", this) === false){
46076                 return false;
46077             }
46078             return true;
46079         }
46080         this.fireEvent("activate", this);
46081         return true;
46082     },
46083     /**
46084      * Updates this panel's element (not for iframe)
46085      * @param {String} content The new content
46086      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46087     */
46088     setContent : function(content, loadScripts){
46089         if (this.iframe) {
46090             return;
46091         }
46092         
46093         this.el.update(content, loadScripts);
46094     },
46095
46096     ignoreResize : function(w, h)
46097     {
46098         //return false; // always resize?
46099         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46100             return true;
46101         }else{
46102             this.lastSize = {width: w, height: h};
46103             return false;
46104         }
46105     },
46106     /**
46107      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46108      * @return {Roo.UpdateManager} The UpdateManager
46109      */
46110     getUpdateManager : function(){
46111         if (this.iframe) {
46112             return false;
46113         }
46114         return this.el.getUpdateManager();
46115     },
46116      /**
46117      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46118      * Does not work with IFRAME contents
46119      * @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:
46120 <pre><code>
46121 panel.load({
46122     url: "your-url.php",
46123     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46124     callback: yourFunction,
46125     scope: yourObject, //(optional scope)
46126     discardUrl: false,
46127     nocache: false,
46128     text: "Loading...",
46129     timeout: 30,
46130     scripts: false
46131 });
46132 </code></pre>
46133      
46134      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46135      * 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.
46136      * @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}
46137      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46138      * @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.
46139      * @return {Roo.ContentPanel} this
46140      */
46141     load : function(){
46142         
46143         if (this.iframe) {
46144             return this;
46145         }
46146         
46147         var um = this.el.getUpdateManager();
46148         um.update.apply(um, arguments);
46149         return this;
46150     },
46151
46152
46153     /**
46154      * 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.
46155      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46156      * @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)
46157      * @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)
46158      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
46159      */
46160     setUrl : function(url, params, loadOnce){
46161         if (this.iframe) {
46162             this.iframeEl.dom.src = url;
46163             return false;
46164         }
46165         
46166         if(this.refreshDelegate){
46167             this.removeListener("activate", this.refreshDelegate);
46168         }
46169         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46170         this.on("activate", this.refreshDelegate);
46171         return this.el.getUpdateManager();
46172     },
46173     
46174     _handleRefresh : function(url, params, loadOnce){
46175         if(!loadOnce || !this.loaded){
46176             var updater = this.el.getUpdateManager();
46177             updater.update(url, params, this._setLoaded.createDelegate(this));
46178         }
46179     },
46180     
46181     _setLoaded : function(){
46182         this.loaded = true;
46183     }, 
46184     
46185     /**
46186      * Returns this panel's id
46187      * @return {String} 
46188      */
46189     getId : function(){
46190         return this.el.id;
46191     },
46192     
46193     /** 
46194      * Returns this panel's element - used by regiosn to add.
46195      * @return {Roo.Element} 
46196      */
46197     getEl : function(){
46198         return this.wrapEl || this.el;
46199     },
46200     
46201    
46202     
46203     adjustForComponents : function(width, height)
46204     {
46205         //Roo.log('adjustForComponents ');
46206         if(this.resizeEl != this.el){
46207             width -= this.el.getFrameWidth('lr');
46208             height -= this.el.getFrameWidth('tb');
46209         }
46210         if(this.toolbar){
46211             var te = this.toolbar.getEl();
46212             te.setWidth(width);
46213             height -= te.getHeight();
46214         }
46215         if(this.footer){
46216             var te = this.footer.getEl();
46217             te.setWidth(width);
46218             height -= te.getHeight();
46219         }
46220         
46221         
46222         if(this.adjustments){
46223             width += this.adjustments[0];
46224             height += this.adjustments[1];
46225         }
46226         return {"width": width, "height": height};
46227     },
46228     
46229     setSize : function(width, height){
46230         if(this.fitToFrame && !this.ignoreResize(width, height)){
46231             if(this.fitContainer && this.resizeEl != this.el){
46232                 this.el.setSize(width, height);
46233             }
46234             var size = this.adjustForComponents(width, height);
46235             if (this.iframe) {
46236                 this.iframeEl.setSize(width,height);
46237             }
46238             
46239             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46240             this.fireEvent('resize', this, size.width, size.height);
46241             
46242             
46243         }
46244     },
46245     
46246     /**
46247      * Returns this panel's title
46248      * @return {String} 
46249      */
46250     getTitle : function(){
46251         
46252         if (typeof(this.title) != 'object') {
46253             return this.title;
46254         }
46255         
46256         var t = '';
46257         for (var k in this.title) {
46258             if (!this.title.hasOwnProperty(k)) {
46259                 continue;
46260             }
46261             
46262             if (k.indexOf('-') >= 0) {
46263                 var s = k.split('-');
46264                 for (var i = 0; i<s.length; i++) {
46265                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
46266                 }
46267             } else {
46268                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
46269             }
46270         }
46271         return t;
46272     },
46273     
46274     /**
46275      * Set this panel's title
46276      * @param {String} title
46277      */
46278     setTitle : function(title){
46279         this.title = title;
46280         if(this.region){
46281             this.region.updatePanelTitle(this, title);
46282         }
46283     },
46284     
46285     /**
46286      * Returns true is this panel was configured to be closable
46287      * @return {Boolean} 
46288      */
46289     isClosable : function(){
46290         return this.closable;
46291     },
46292     
46293     beforeSlide : function(){
46294         this.el.clip();
46295         this.resizeEl.clip();
46296     },
46297     
46298     afterSlide : function(){
46299         this.el.unclip();
46300         this.resizeEl.unclip();
46301     },
46302     
46303     /**
46304      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46305      *   Will fail silently if the {@link #setUrl} method has not been called.
46306      *   This does not activate the panel, just updates its content.
46307      */
46308     refresh : function(){
46309         if(this.refreshDelegate){
46310            this.loaded = false;
46311            this.refreshDelegate();
46312         }
46313     },
46314     
46315     /**
46316      * Destroys this panel
46317      */
46318     destroy : function(){
46319         this.el.removeAllListeners();
46320         var tempEl = document.createElement("span");
46321         tempEl.appendChild(this.el.dom);
46322         tempEl.innerHTML = "";
46323         this.el.remove();
46324         this.el = null;
46325     },
46326     
46327     /**
46328      * form - if the content panel contains a form - this is a reference to it.
46329      * @type {Roo.form.Form}
46330      */
46331     form : false,
46332     /**
46333      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46334      *    This contains a reference to it.
46335      * @type {Roo.View}
46336      */
46337     view : false,
46338     
46339       /**
46340      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46341      * <pre><code>
46342
46343 layout.addxtype({
46344        xtype : 'Form',
46345        items: [ .... ]
46346    }
46347 );
46348
46349 </code></pre>
46350      * @param {Object} cfg Xtype definition of item to add.
46351      */
46352     
46353     
46354     getChildContainer: function () {
46355         return this.getEl();
46356     },
46357     
46358     
46359     onScroll : function(e)
46360     {
46361         this.fireEvent('scroll', this, e);
46362     }
46363     
46364     
46365     /*
46366         var  ret = new Roo.factory(cfg);
46367         return ret;
46368         
46369         
46370         // add form..
46371         if (cfg.xtype.match(/^Form$/)) {
46372             
46373             var el;
46374             //if (this.footer) {
46375             //    el = this.footer.container.insertSibling(false, 'before');
46376             //} else {
46377                 el = this.el.createChild();
46378             //}
46379
46380             this.form = new  Roo.form.Form(cfg);
46381             
46382             
46383             if ( this.form.allItems.length) {
46384                 this.form.render(el.dom);
46385             }
46386             return this.form;
46387         }
46388         // should only have one of theses..
46389         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46390             // views.. should not be just added - used named prop 'view''
46391             
46392             cfg.el = this.el.appendChild(document.createElement("div"));
46393             // factory?
46394             
46395             var ret = new Roo.factory(cfg);
46396              
46397              ret.render && ret.render(false, ''); // render blank..
46398             this.view = ret;
46399             return ret;
46400         }
46401         return false;
46402     }
46403     \*/
46404 });
46405  
46406 /**
46407  * @class Roo.bootstrap.panel.Grid
46408  * @extends Roo.bootstrap.panel.Content
46409  * @constructor
46410  * Create a new GridPanel.
46411  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
46412  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
46413  * @param {Object} config A the config object
46414   
46415  */
46416
46417
46418
46419 Roo.bootstrap.panel.Grid = function(config)
46420 {
46421     
46422       
46423     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46424         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
46425
46426     config.el = this.wrapper;
46427     //this.el = this.wrapper;
46428     
46429       if (config.container) {
46430         // ctor'ed from a Border/panel.grid
46431         
46432         
46433         this.wrapper.setStyle("overflow", "hidden");
46434         this.wrapper.addClass('roo-grid-container');
46435
46436     }
46437     
46438     
46439     if(config.toolbar){
46440         var tool_el = this.wrapper.createChild();    
46441         this.toolbar = Roo.factory(config.toolbar);
46442         var ti = [];
46443         if (config.toolbar.items) {
46444             ti = config.toolbar.items ;
46445             delete config.toolbar.items ;
46446         }
46447         
46448         var nitems = [];
46449         this.toolbar.render(tool_el);
46450         for(var i =0;i < ti.length;i++) {
46451           //  Roo.log(['add child', items[i]]);
46452             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
46453         }
46454         this.toolbar.items = nitems;
46455         
46456         delete config.toolbar;
46457     }
46458     
46459     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
46460     config.grid.scrollBody = true;;
46461     config.grid.monitorWindowResize = false; // turn off autosizing
46462     config.grid.autoHeight = false;
46463     config.grid.autoWidth = false;
46464     
46465     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
46466     
46467     if (config.background) {
46468         // render grid on panel activation (if panel background)
46469         this.on('activate', function(gp) {
46470             if (!gp.grid.rendered) {
46471                 gp.grid.render(this.wrapper);
46472                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
46473             }
46474         });
46475             
46476     } else {
46477         this.grid.render(this.wrapper);
46478         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
46479
46480     }
46481     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
46482     // ??? needed ??? config.el = this.wrapper;
46483     
46484     
46485     
46486   
46487     // xtype created footer. - not sure if will work as we normally have to render first..
46488     if (this.footer && !this.footer.el && this.footer.xtype) {
46489         
46490         var ctr = this.grid.getView().getFooterPanel(true);
46491         this.footer.dataSource = this.grid.dataSource;
46492         this.footer = Roo.factory(this.footer, Roo);
46493         this.footer.render(ctr);
46494         
46495     }
46496     
46497     
46498     
46499     
46500      
46501 };
46502
46503 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
46504 {
46505   
46506     getId : function(){
46507         return this.grid.id;
46508     },
46509     
46510     /**
46511      * Returns the grid for this panel
46512      * @return {Roo.bootstrap.Table} 
46513      */
46514     getGrid : function(){
46515         return this.grid;    
46516     },
46517     
46518     setSize : function(width, height)
46519     {
46520      
46521         //if(!this.ignoreResize(width, height)){
46522             var grid = this.grid;
46523             var size = this.adjustForComponents(width, height);
46524             // tfoot is not a footer?
46525           
46526             
46527             var gridel = grid.getGridEl();
46528             gridel.setSize(size.width, size.height);
46529             
46530             var tbd = grid.getGridEl().select('tbody', true).first();
46531             var thd = grid.getGridEl().select('thead',true).first();
46532             var tbf= grid.getGridEl().select('tfoot', true).first();
46533
46534             if (tbf) {
46535                 size.height -= tbf.getHeight();
46536             }
46537             if (thd) {
46538                 size.height -= thd.getHeight();
46539             }
46540             
46541             tbd.setSize(size.width, size.height );
46542             // this is for the account management tab -seems to work there.
46543             var thd = grid.getGridEl().select('thead',true).first();
46544             //if (tbd) {
46545             //    tbd.setSize(size.width, size.height - thd.getHeight());
46546             //}
46547              
46548             grid.autoSize();
46549         //}
46550    
46551     },
46552      
46553     
46554     
46555     beforeSlide : function(){
46556         this.grid.getView().scroller.clip();
46557     },
46558     
46559     afterSlide : function(){
46560         this.grid.getView().scroller.unclip();
46561     },
46562     
46563     destroy : function(){
46564         this.grid.destroy();
46565         delete this.grid;
46566         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
46567     }
46568 });
46569
46570 /**
46571  * @class Roo.bootstrap.panel.Nest
46572  * @extends Roo.bootstrap.panel.Content
46573  * @constructor
46574  * Create a new Panel, that can contain a layout.Border.
46575  * 
46576  * 
46577  * @param {String/Object} config A string to set only the title or a config object
46578  */
46579 Roo.bootstrap.panel.Nest = function(config)
46580 {
46581     // construct with only one argument..
46582     /* FIXME - implement nicer consturctors
46583     if (layout.layout) {
46584         config = layout;
46585         layout = config.layout;
46586         delete config.layout;
46587     }
46588     if (layout.xtype && !layout.getEl) {
46589         // then layout needs constructing..
46590         layout = Roo.factory(layout, Roo);
46591     }
46592     */
46593     
46594     config.el =  config.layout.getEl();
46595     
46596     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46597     
46598     config.layout.monitorWindowResize = false; // turn off autosizing
46599     this.layout = config.layout;
46600     this.layout.getEl().addClass("roo-layout-nested-layout");
46601     this.layout.parent = this;
46602     
46603     
46604     
46605     
46606 };
46607
46608 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46609     /**
46610     * @cfg {Roo.BorderLayout} layout The layout for this panel
46611     */
46612     layout : false,
46613
46614     setSize : function(width, height){
46615         if(!this.ignoreResize(width, height)){
46616             var size = this.adjustForComponents(width, height);
46617             var el = this.layout.getEl();
46618             if (size.height < 1) {
46619                 el.setWidth(size.width);   
46620             } else {
46621                 el.setSize(size.width, size.height);
46622             }
46623             var touch = el.dom.offsetWidth;
46624             this.layout.layout();
46625             // ie requires a double layout on the first pass
46626             if(Roo.isIE && !this.initialized){
46627                 this.initialized = true;
46628                 this.layout.layout();
46629             }
46630         }
46631     },
46632     
46633     // activate all subpanels if not currently active..
46634     
46635     setActiveState : function(active){
46636         this.active = active;
46637         this.setActiveClass(active);
46638         
46639         if(!active){
46640             this.fireEvent("deactivate", this);
46641             return;
46642         }
46643         
46644         this.fireEvent("activate", this);
46645         // not sure if this should happen before or after..
46646         if (!this.layout) {
46647             return; // should not happen..
46648         }
46649         var reg = false;
46650         for (var r in this.layout.regions) {
46651             reg = this.layout.getRegion(r);
46652             if (reg.getActivePanel()) {
46653                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46654                 reg.setActivePanel(reg.getActivePanel());
46655                 continue;
46656             }
46657             if (!reg.panels.length) {
46658                 continue;
46659             }
46660             reg.showPanel(reg.getPanel(0));
46661         }
46662         
46663         
46664         
46665         
46666     },
46667     
46668     /**
46669      * Returns the nested BorderLayout for this panel
46670      * @return {Roo.BorderLayout} 
46671      */
46672     getLayout : function(){
46673         return this.layout;
46674     },
46675     
46676      /**
46677      * Adds a xtype elements to the layout of the nested panel
46678      * <pre><code>
46679
46680 panel.addxtype({
46681        xtype : 'ContentPanel',
46682        region: 'west',
46683        items: [ .... ]
46684    }
46685 );
46686
46687 panel.addxtype({
46688         xtype : 'NestedLayoutPanel',
46689         region: 'west',
46690         layout: {
46691            center: { },
46692            west: { }   
46693         },
46694         items : [ ... list of content panels or nested layout panels.. ]
46695    }
46696 );
46697 </code></pre>
46698      * @param {Object} cfg Xtype definition of item to add.
46699      */
46700     addxtype : function(cfg) {
46701         return this.layout.addxtype(cfg);
46702     
46703     }
46704 });/*
46705  * Based on:
46706  * Ext JS Library 1.1.1
46707  * Copyright(c) 2006-2007, Ext JS, LLC.
46708  *
46709  * Originally Released Under LGPL - original licence link has changed is not relivant.
46710  *
46711  * Fork - LGPL
46712  * <script type="text/javascript">
46713  */
46714 /**
46715  * @class Roo.TabPanel
46716  * @extends Roo.util.Observable
46717  * A lightweight tab container.
46718  * <br><br>
46719  * Usage:
46720  * <pre><code>
46721 // basic tabs 1, built from existing content
46722 var tabs = new Roo.TabPanel("tabs1");
46723 tabs.addTab("script", "View Script");
46724 tabs.addTab("markup", "View Markup");
46725 tabs.activate("script");
46726
46727 // more advanced tabs, built from javascript
46728 var jtabs = new Roo.TabPanel("jtabs");
46729 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46730
46731 // set up the UpdateManager
46732 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46733 var updater = tab2.getUpdateManager();
46734 updater.setDefaultUrl("ajax1.htm");
46735 tab2.on('activate', updater.refresh, updater, true);
46736
46737 // Use setUrl for Ajax loading
46738 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46739 tab3.setUrl("ajax2.htm", null, true);
46740
46741 // Disabled tab
46742 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46743 tab4.disable();
46744
46745 jtabs.activate("jtabs-1");
46746  * </code></pre>
46747  * @constructor
46748  * Create a new TabPanel.
46749  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46750  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46751  */
46752 Roo.bootstrap.panel.Tabs = function(config){
46753     /**
46754     * The container element for this TabPanel.
46755     * @type Roo.Element
46756     */
46757     this.el = Roo.get(config.el);
46758     delete config.el;
46759     if(config){
46760         if(typeof config == "boolean"){
46761             this.tabPosition = config ? "bottom" : "top";
46762         }else{
46763             Roo.apply(this, config);
46764         }
46765     }
46766     
46767     if(this.tabPosition == "bottom"){
46768         // if tabs are at the bottom = create the body first.
46769         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46770         this.el.addClass("roo-tabs-bottom");
46771     }
46772     // next create the tabs holders
46773     
46774     if (this.tabPosition == "west"){
46775         
46776         var reg = this.region; // fake it..
46777         while (reg) {
46778             if (!reg.mgr.parent) {
46779                 break;
46780             }
46781             reg = reg.mgr.parent.region;
46782         }
46783         Roo.log("got nest?");
46784         Roo.log(reg);
46785         if (reg.mgr.getRegion('west')) {
46786             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46787             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46788             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46789             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46790             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46791         
46792             
46793         }
46794         
46795         
46796     } else {
46797      
46798         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46799         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46800         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46801         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46802     }
46803     
46804     
46805     if(Roo.isIE){
46806         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46807     }
46808     
46809     // finally - if tabs are at the top, then create the body last..
46810     if(this.tabPosition != "bottom"){
46811         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46812          * @type Roo.Element
46813          */
46814         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46815         this.el.addClass("roo-tabs-top");
46816     }
46817     this.items = [];
46818
46819     this.bodyEl.setStyle("position", "relative");
46820
46821     this.active = null;
46822     this.activateDelegate = this.activate.createDelegate(this);
46823
46824     this.addEvents({
46825         /**
46826          * @event tabchange
46827          * Fires when the active tab changes
46828          * @param {Roo.TabPanel} this
46829          * @param {Roo.TabPanelItem} activePanel The new active tab
46830          */
46831         "tabchange": true,
46832         /**
46833          * @event beforetabchange
46834          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46835          * @param {Roo.TabPanel} this
46836          * @param {Object} e Set cancel to true on this object to cancel the tab change
46837          * @param {Roo.TabPanelItem} tab The tab being changed to
46838          */
46839         "beforetabchange" : true
46840     });
46841
46842     Roo.EventManager.onWindowResize(this.onResize, this);
46843     this.cpad = this.el.getPadding("lr");
46844     this.hiddenCount = 0;
46845
46846
46847     // toolbar on the tabbar support...
46848     if (this.toolbar) {
46849         alert("no toolbar support yet");
46850         this.toolbar  = false;
46851         /*
46852         var tcfg = this.toolbar;
46853         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
46854         this.toolbar = new Roo.Toolbar(tcfg);
46855         if (Roo.isSafari) {
46856             var tbl = tcfg.container.child('table', true);
46857             tbl.setAttribute('width', '100%');
46858         }
46859         */
46860         
46861     }
46862    
46863
46864
46865     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46866 };
46867
46868 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46869     /*
46870      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46871      */
46872     tabPosition : "top",
46873     /*
46874      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46875      */
46876     currentTabWidth : 0,
46877     /*
46878      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46879      */
46880     minTabWidth : 40,
46881     /*
46882      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46883      */
46884     maxTabWidth : 250,
46885     /*
46886      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46887      */
46888     preferredTabWidth : 175,
46889     /*
46890      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46891      */
46892     resizeTabs : false,
46893     /*
46894      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46895      */
46896     monitorResize : true,
46897     /*
46898      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
46899      */
46900     toolbar : false,  // set by caller..
46901     
46902     region : false, /// set by caller
46903     
46904     disableTooltips : true, // not used yet...
46905
46906     /**
46907      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46908      * @param {String} id The id of the div to use <b>or create</b>
46909      * @param {String} text The text for the tab
46910      * @param {String} content (optional) Content to put in the TabPanelItem body
46911      * @param {Boolean} closable (optional) True to create a close icon on the tab
46912      * @return {Roo.TabPanelItem} The created TabPanelItem
46913      */
46914     addTab : function(id, text, content, closable, tpl)
46915     {
46916         var item = new Roo.bootstrap.panel.TabItem({
46917             panel: this,
46918             id : id,
46919             text : text,
46920             closable : closable,
46921             tpl : tpl
46922         });
46923         this.addTabItem(item);
46924         if(content){
46925             item.setContent(content);
46926         }
46927         return item;
46928     },
46929
46930     /**
46931      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46932      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46933      * @return {Roo.TabPanelItem}
46934      */
46935     getTab : function(id){
46936         return this.items[id];
46937     },
46938
46939     /**
46940      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46941      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46942      */
46943     hideTab : function(id){
46944         var t = this.items[id];
46945         if(!t.isHidden()){
46946            t.setHidden(true);
46947            this.hiddenCount++;
46948            this.autoSizeTabs();
46949         }
46950     },
46951
46952     /**
46953      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46954      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46955      */
46956     unhideTab : function(id){
46957         var t = this.items[id];
46958         if(t.isHidden()){
46959            t.setHidden(false);
46960            this.hiddenCount--;
46961            this.autoSizeTabs();
46962         }
46963     },
46964
46965     /**
46966      * Adds an existing {@link Roo.TabPanelItem}.
46967      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46968      */
46969     addTabItem : function(item)
46970     {
46971         this.items[item.id] = item;
46972         this.items.push(item);
46973         this.autoSizeTabs();
46974       //  if(this.resizeTabs){
46975     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46976   //         this.autoSizeTabs();
46977 //        }else{
46978 //            item.autoSize();
46979        // }
46980     },
46981
46982     /**
46983      * Removes a {@link Roo.TabPanelItem}.
46984      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46985      */
46986     removeTab : function(id){
46987         var items = this.items;
46988         var tab = items[id];
46989         if(!tab) { return; }
46990         var index = items.indexOf(tab);
46991         if(this.active == tab && items.length > 1){
46992             var newTab = this.getNextAvailable(index);
46993             if(newTab) {
46994                 newTab.activate();
46995             }
46996         }
46997         this.stripEl.dom.removeChild(tab.pnode.dom);
46998         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46999             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
47000         }
47001         items.splice(index, 1);
47002         delete this.items[tab.id];
47003         tab.fireEvent("close", tab);
47004         tab.purgeListeners();
47005         this.autoSizeTabs();
47006     },
47007
47008     getNextAvailable : function(start){
47009         var items = this.items;
47010         var index = start;
47011         // look for a next tab that will slide over to
47012         // replace the one being removed
47013         while(index < items.length){
47014             var item = items[++index];
47015             if(item && !item.isHidden()){
47016                 return item;
47017             }
47018         }
47019         // if one isn't found select the previous tab (on the left)
47020         index = start;
47021         while(index >= 0){
47022             var item = items[--index];
47023             if(item && !item.isHidden()){
47024                 return item;
47025             }
47026         }
47027         return null;
47028     },
47029
47030     /**
47031      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
47032      * @param {String/Number} id The id or index of the TabPanelItem to disable.
47033      */
47034     disableTab : function(id){
47035         var tab = this.items[id];
47036         if(tab && this.active != tab){
47037             tab.disable();
47038         }
47039     },
47040
47041     /**
47042      * Enables a {@link Roo.TabPanelItem} that is disabled.
47043      * @param {String/Number} id The id or index of the TabPanelItem to enable.
47044      */
47045     enableTab : function(id){
47046         var tab = this.items[id];
47047         tab.enable();
47048     },
47049
47050     /**
47051      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
47052      * @param {String/Number} id The id or index of the TabPanelItem to activate.
47053      * @return {Roo.TabPanelItem} The TabPanelItem.
47054      */
47055     activate : function(id)
47056     {
47057         //Roo.log('activite:'  + id);
47058         
47059         var tab = this.items[id];
47060         if(!tab){
47061             return null;
47062         }
47063         if(tab == this.active || tab.disabled){
47064             return tab;
47065         }
47066         var e = {};
47067         this.fireEvent("beforetabchange", this, e, tab);
47068         if(e.cancel !== true && !tab.disabled){
47069             if(this.active){
47070                 this.active.hide();
47071             }
47072             this.active = this.items[id];
47073             this.active.show();
47074             this.fireEvent("tabchange", this, this.active);
47075         }
47076         return tab;
47077     },
47078
47079     /**
47080      * Gets the active {@link Roo.TabPanelItem}.
47081      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
47082      */
47083     getActiveTab : function(){
47084         return this.active;
47085     },
47086
47087     /**
47088      * Updates the tab body element to fit the height of the container element
47089      * for overflow scrolling
47090      * @param {Number} targetHeight (optional) Override the starting height from the elements height
47091      */
47092     syncHeight : function(targetHeight){
47093         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47094         var bm = this.bodyEl.getMargins();
47095         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
47096         this.bodyEl.setHeight(newHeight);
47097         return newHeight;
47098     },
47099
47100     onResize : function(){
47101         if(this.monitorResize){
47102             this.autoSizeTabs();
47103         }
47104     },
47105
47106     /**
47107      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
47108      */
47109     beginUpdate : function(){
47110         this.updating = true;
47111     },
47112
47113     /**
47114      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
47115      */
47116     endUpdate : function(){
47117         this.updating = false;
47118         this.autoSizeTabs();
47119     },
47120
47121     /**
47122      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
47123      */
47124     autoSizeTabs : function()
47125     {
47126         var count = this.items.length;
47127         var vcount = count - this.hiddenCount;
47128         
47129         if (vcount < 2) {
47130             this.stripEl.hide();
47131         } else {
47132             this.stripEl.show();
47133         }
47134         
47135         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
47136             return;
47137         }
47138         
47139         
47140         var w = Math.max(this.el.getWidth() - this.cpad, 10);
47141         var availWidth = Math.floor(w / vcount);
47142         var b = this.stripBody;
47143         if(b.getWidth() > w){
47144             var tabs = this.items;
47145             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
47146             if(availWidth < this.minTabWidth){
47147                 /*if(!this.sleft){    // incomplete scrolling code
47148                     this.createScrollButtons();
47149                 }
47150                 this.showScroll();
47151                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
47152             }
47153         }else{
47154             if(this.currentTabWidth < this.preferredTabWidth){
47155                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
47156             }
47157         }
47158     },
47159
47160     /**
47161      * Returns the number of tabs in this TabPanel.
47162      * @return {Number}
47163      */
47164      getCount : function(){
47165          return this.items.length;
47166      },
47167
47168     /**
47169      * Resizes all the tabs to the passed width
47170      * @param {Number} The new width
47171      */
47172     setTabWidth : function(width){
47173         this.currentTabWidth = width;
47174         for(var i = 0, len = this.items.length; i < len; i++) {
47175                 if(!this.items[i].isHidden()) {
47176                 this.items[i].setWidth(width);
47177             }
47178         }
47179     },
47180
47181     /**
47182      * Destroys this TabPanel
47183      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
47184      */
47185     destroy : function(removeEl){
47186         Roo.EventManager.removeResizeListener(this.onResize, this);
47187         for(var i = 0, len = this.items.length; i < len; i++){
47188             this.items[i].purgeListeners();
47189         }
47190         if(removeEl === true){
47191             this.el.update("");
47192             this.el.remove();
47193         }
47194     },
47195     
47196     createStrip : function(container)
47197     {
47198         var strip = document.createElement("nav");
47199         strip.className = Roo.bootstrap.version == 4 ?
47200             "navbar-light bg-light" : 
47201             "navbar navbar-default"; //"x-tabs-wrap";
47202         container.appendChild(strip);
47203         return strip;
47204     },
47205     
47206     createStripList : function(strip)
47207     {
47208         // div wrapper for retard IE
47209         // returns the "tr" element.
47210         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
47211         //'<div class="x-tabs-strip-wrap">'+
47212           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
47213           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
47214         return strip.firstChild; //.firstChild.firstChild.firstChild;
47215     },
47216     createBody : function(container)
47217     {
47218         var body = document.createElement("div");
47219         Roo.id(body, "tab-body");
47220         //Roo.fly(body).addClass("x-tabs-body");
47221         Roo.fly(body).addClass("tab-content");
47222         container.appendChild(body);
47223         return body;
47224     },
47225     createItemBody :function(bodyEl, id){
47226         var body = Roo.getDom(id);
47227         if(!body){
47228             body = document.createElement("div");
47229             body.id = id;
47230         }
47231         //Roo.fly(body).addClass("x-tabs-item-body");
47232         Roo.fly(body).addClass("tab-pane");
47233          bodyEl.insertBefore(body, bodyEl.firstChild);
47234         return body;
47235     },
47236     /** @private */
47237     createStripElements :  function(stripEl, text, closable, tpl)
47238     {
47239         var td = document.createElement("li"); // was td..
47240         td.className = 'nav-item';
47241         
47242         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
47243         
47244         
47245         stripEl.appendChild(td);
47246         /*if(closable){
47247             td.className = "x-tabs-closable";
47248             if(!this.closeTpl){
47249                 this.closeTpl = new Roo.Template(
47250                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
47251                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
47252                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
47253                 );
47254             }
47255             var el = this.closeTpl.overwrite(td, {"text": text});
47256             var close = el.getElementsByTagName("div")[0];
47257             var inner = el.getElementsByTagName("em")[0];
47258             return {"el": el, "close": close, "inner": inner};
47259         } else {
47260         */
47261         // not sure what this is..
47262 //            if(!this.tabTpl){
47263                 //this.tabTpl = new Roo.Template(
47264                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
47265                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
47266                 //);
47267 //                this.tabTpl = new Roo.Template(
47268 //                   '<a href="#">' +
47269 //                   '<span unselectable="on"' +
47270 //                            (this.disableTooltips ? '' : ' title="{text}"') +
47271 //                            ' >{text}</span></a>'
47272 //                );
47273 //                
47274 //            }
47275
47276
47277             var template = tpl || this.tabTpl || false;
47278             
47279             if(!template){
47280                 template =  new Roo.Template(
47281                         Roo.bootstrap.version == 4 ? 
47282                             (
47283                                 '<a class="nav-link" href="#" unselectable="on"' +
47284                                      (this.disableTooltips ? '' : ' title="{text}"') +
47285                                      ' >{text}</a>'
47286                             ) : (
47287                                 '<a class="nav-link" href="#">' +
47288                                 '<span unselectable="on"' +
47289                                          (this.disableTooltips ? '' : ' title="{text}"') +
47290                                     ' >{text}</span></a>'
47291                             )
47292                 );
47293             }
47294             
47295             switch (typeof(template)) {
47296                 case 'object' :
47297                     break;
47298                 case 'string' :
47299                     template = new Roo.Template(template);
47300                     break;
47301                 default :
47302                     break;
47303             }
47304             
47305             var el = template.overwrite(td, {"text": text});
47306             
47307             var inner = el.getElementsByTagName("span")[0];
47308             
47309             return {"el": el, "inner": inner};
47310             
47311     }
47312         
47313     
47314 });
47315
47316 /**
47317  * @class Roo.TabPanelItem
47318  * @extends Roo.util.Observable
47319  * Represents an individual item (tab plus body) in a TabPanel.
47320  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
47321  * @param {String} id The id of this TabPanelItem
47322  * @param {String} text The text for the tab of this TabPanelItem
47323  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
47324  */
47325 Roo.bootstrap.panel.TabItem = function(config){
47326     /**
47327      * The {@link Roo.TabPanel} this TabPanelItem belongs to
47328      * @type Roo.TabPanel
47329      */
47330     this.tabPanel = config.panel;
47331     /**
47332      * The id for this TabPanelItem
47333      * @type String
47334      */
47335     this.id = config.id;
47336     /** @private */
47337     this.disabled = false;
47338     /** @private */
47339     this.text = config.text;
47340     /** @private */
47341     this.loaded = false;
47342     this.closable = config.closable;
47343
47344     /**
47345      * The body element for this TabPanelItem.
47346      * @type Roo.Element
47347      */
47348     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
47349     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
47350     this.bodyEl.setStyle("display", "block");
47351     this.bodyEl.setStyle("zoom", "1");
47352     //this.hideAction();
47353
47354     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
47355     /** @private */
47356     this.el = Roo.get(els.el);
47357     this.inner = Roo.get(els.inner, true);
47358      this.textEl = Roo.bootstrap.version == 4 ?
47359         this.el : Roo.get(this.el.dom.firstChild, true);
47360
47361     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
47362     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
47363
47364     
47365 //    this.el.on("mousedown", this.onTabMouseDown, this);
47366     this.el.on("click", this.onTabClick, this);
47367     /** @private */
47368     if(config.closable){
47369         var c = Roo.get(els.close, true);
47370         c.dom.title = this.closeText;
47371         c.addClassOnOver("close-over");
47372         c.on("click", this.closeClick, this);
47373      }
47374
47375     this.addEvents({
47376          /**
47377          * @event activate
47378          * Fires when this tab becomes the active tab.
47379          * @param {Roo.TabPanel} tabPanel The parent TabPanel
47380          * @param {Roo.TabPanelItem} this
47381          */
47382         "activate": true,
47383         /**
47384          * @event beforeclose
47385          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
47386          * @param {Roo.TabPanelItem} this
47387          * @param {Object} e Set cancel to true on this object to cancel the close.
47388          */
47389         "beforeclose": true,
47390         /**
47391          * @event close
47392          * Fires when this tab is closed.
47393          * @param {Roo.TabPanelItem} this
47394          */
47395          "close": true,
47396         /**
47397          * @event deactivate
47398          * Fires when this tab is no longer the active tab.
47399          * @param {Roo.TabPanel} tabPanel The parent TabPanel
47400          * @param {Roo.TabPanelItem} this
47401          */
47402          "deactivate" : true
47403     });
47404     this.hidden = false;
47405
47406     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
47407 };
47408
47409 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
47410            {
47411     purgeListeners : function(){
47412        Roo.util.Observable.prototype.purgeListeners.call(this);
47413        this.el.removeAllListeners();
47414     },
47415     /**
47416      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
47417      */
47418     show : function(){
47419         this.status_node.addClass("active");
47420         this.showAction();
47421         if(Roo.isOpera){
47422             this.tabPanel.stripWrap.repaint();
47423         }
47424         this.fireEvent("activate", this.tabPanel, this);
47425     },
47426
47427     /**
47428      * Returns true if this tab is the active tab.
47429      * @return {Boolean}
47430      */
47431     isActive : function(){
47432         return this.tabPanel.getActiveTab() == this;
47433     },
47434
47435     /**
47436      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
47437      */
47438     hide : function(){
47439         this.status_node.removeClass("active");
47440         this.hideAction();
47441         this.fireEvent("deactivate", this.tabPanel, this);
47442     },
47443
47444     hideAction : function(){
47445         this.bodyEl.hide();
47446         this.bodyEl.setStyle("position", "absolute");
47447         this.bodyEl.setLeft("-20000px");
47448         this.bodyEl.setTop("-20000px");
47449     },
47450
47451     showAction : function(){
47452         this.bodyEl.setStyle("position", "relative");
47453         this.bodyEl.setTop("");
47454         this.bodyEl.setLeft("");
47455         this.bodyEl.show();
47456     },
47457
47458     /**
47459      * Set the tooltip for the tab.
47460      * @param {String} tooltip The tab's tooltip
47461      */
47462     setTooltip : function(text){
47463         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
47464             this.textEl.dom.qtip = text;
47465             this.textEl.dom.removeAttribute('title');
47466         }else{
47467             this.textEl.dom.title = text;
47468         }
47469     },
47470
47471     onTabClick : function(e){
47472         e.preventDefault();
47473         this.tabPanel.activate(this.id);
47474     },
47475
47476     onTabMouseDown : function(e){
47477         e.preventDefault();
47478         this.tabPanel.activate(this.id);
47479     },
47480 /*
47481     getWidth : function(){
47482         return this.inner.getWidth();
47483     },
47484
47485     setWidth : function(width){
47486         var iwidth = width - this.linode.getPadding("lr");
47487         this.inner.setWidth(iwidth);
47488         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
47489         this.linode.setWidth(width);
47490     },
47491 */
47492     /**
47493      * Show or hide the tab
47494      * @param {Boolean} hidden True to hide or false to show.
47495      */
47496     setHidden : function(hidden){
47497         this.hidden = hidden;
47498         this.linode.setStyle("display", hidden ? "none" : "");
47499     },
47500
47501     /**
47502      * Returns true if this tab is "hidden"
47503      * @return {Boolean}
47504      */
47505     isHidden : function(){
47506         return this.hidden;
47507     },
47508
47509     /**
47510      * Returns the text for this tab
47511      * @return {String}
47512      */
47513     getText : function(){
47514         return this.text;
47515     },
47516     /*
47517     autoSize : function(){
47518         //this.el.beginMeasure();
47519         this.textEl.setWidth(1);
47520         /*
47521          *  #2804 [new] Tabs in Roojs
47522          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
47523          */
47524         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
47525         //this.el.endMeasure();
47526     //},
47527
47528     /**
47529      * Sets the text for the tab (Note: this also sets the tooltip text)
47530      * @param {String} text The tab's text and tooltip
47531      */
47532     setText : function(text){
47533         this.text = text;
47534         this.textEl.update(text);
47535         this.setTooltip(text);
47536         //if(!this.tabPanel.resizeTabs){
47537         //    this.autoSize();
47538         //}
47539     },
47540     /**
47541      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
47542      */
47543     activate : function(){
47544         this.tabPanel.activate(this.id);
47545     },
47546
47547     /**
47548      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
47549      */
47550     disable : function(){
47551         if(this.tabPanel.active != this){
47552             this.disabled = true;
47553             this.status_node.addClass("disabled");
47554         }
47555     },
47556
47557     /**
47558      * Enables this TabPanelItem if it was previously disabled.
47559      */
47560     enable : function(){
47561         this.disabled = false;
47562         this.status_node.removeClass("disabled");
47563     },
47564
47565     /**
47566      * Sets the content for this TabPanelItem.
47567      * @param {String} content The content
47568      * @param {Boolean} loadScripts true to look for and load scripts
47569      */
47570     setContent : function(content, loadScripts){
47571         this.bodyEl.update(content, loadScripts);
47572     },
47573
47574     /**
47575      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47576      * @return {Roo.UpdateManager} The UpdateManager
47577      */
47578     getUpdateManager : function(){
47579         return this.bodyEl.getUpdateManager();
47580     },
47581
47582     /**
47583      * Set a URL to be used to load the content for this TabPanelItem.
47584      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47585      * @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)
47586      * @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)
47587      * @return {Roo.UpdateManager} The UpdateManager
47588      */
47589     setUrl : function(url, params, loadOnce){
47590         if(this.refreshDelegate){
47591             this.un('activate', this.refreshDelegate);
47592         }
47593         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47594         this.on("activate", this.refreshDelegate);
47595         return this.bodyEl.getUpdateManager();
47596     },
47597
47598     /** @private */
47599     _handleRefresh : function(url, params, loadOnce){
47600         if(!loadOnce || !this.loaded){
47601             var updater = this.bodyEl.getUpdateManager();
47602             updater.update(url, params, this._setLoaded.createDelegate(this));
47603         }
47604     },
47605
47606     /**
47607      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
47608      *   Will fail silently if the setUrl method has not been called.
47609      *   This does not activate the panel, just updates its content.
47610      */
47611     refresh : function(){
47612         if(this.refreshDelegate){
47613            this.loaded = false;
47614            this.refreshDelegate();
47615         }
47616     },
47617
47618     /** @private */
47619     _setLoaded : function(){
47620         this.loaded = true;
47621     },
47622
47623     /** @private */
47624     closeClick : function(e){
47625         var o = {};
47626         e.stopEvent();
47627         this.fireEvent("beforeclose", this, o);
47628         if(o.cancel !== true){
47629             this.tabPanel.removeTab(this.id);
47630         }
47631     },
47632     /**
47633      * The text displayed in the tooltip for the close icon.
47634      * @type String
47635      */
47636     closeText : "Close this tab"
47637 });
47638 /**
47639 *    This script refer to:
47640 *    Title: International Telephone Input
47641 *    Author: Jack O'Connor
47642 *    Code version:  v12.1.12
47643 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47644 **/
47645
47646 Roo.bootstrap.form.PhoneInputData = function() {
47647     var d = [
47648       [
47649         "Afghanistan (‫افغانستان‬‎)",
47650         "af",
47651         "93"
47652       ],
47653       [
47654         "Albania (Shqipëri)",
47655         "al",
47656         "355"
47657       ],
47658       [
47659         "Algeria (‫الجزائر‬‎)",
47660         "dz",
47661         "213"
47662       ],
47663       [
47664         "American Samoa",
47665         "as",
47666         "1684"
47667       ],
47668       [
47669         "Andorra",
47670         "ad",
47671         "376"
47672       ],
47673       [
47674         "Angola",
47675         "ao",
47676         "244"
47677       ],
47678       [
47679         "Anguilla",
47680         "ai",
47681         "1264"
47682       ],
47683       [
47684         "Antigua and Barbuda",
47685         "ag",
47686         "1268"
47687       ],
47688       [
47689         "Argentina",
47690         "ar",
47691         "54"
47692       ],
47693       [
47694         "Armenia (Հայաստան)",
47695         "am",
47696         "374"
47697       ],
47698       [
47699         "Aruba",
47700         "aw",
47701         "297"
47702       ],
47703       [
47704         "Australia",
47705         "au",
47706         "61",
47707         0
47708       ],
47709       [
47710         "Austria (Österreich)",
47711         "at",
47712         "43"
47713       ],
47714       [
47715         "Azerbaijan (Azərbaycan)",
47716         "az",
47717         "994"
47718       ],
47719       [
47720         "Bahamas",
47721         "bs",
47722         "1242"
47723       ],
47724       [
47725         "Bahrain (‫البحرين‬‎)",
47726         "bh",
47727         "973"
47728       ],
47729       [
47730         "Bangladesh (বাংলাদেশ)",
47731         "bd",
47732         "880"
47733       ],
47734       [
47735         "Barbados",
47736         "bb",
47737         "1246"
47738       ],
47739       [
47740         "Belarus (Беларусь)",
47741         "by",
47742         "375"
47743       ],
47744       [
47745         "Belgium (België)",
47746         "be",
47747         "32"
47748       ],
47749       [
47750         "Belize",
47751         "bz",
47752         "501"
47753       ],
47754       [
47755         "Benin (Bénin)",
47756         "bj",
47757         "229"
47758       ],
47759       [
47760         "Bermuda",
47761         "bm",
47762         "1441"
47763       ],
47764       [
47765         "Bhutan (འབྲུག)",
47766         "bt",
47767         "975"
47768       ],
47769       [
47770         "Bolivia",
47771         "bo",
47772         "591"
47773       ],
47774       [
47775         "Bosnia and Herzegovina (Босна и Херцеговина)",
47776         "ba",
47777         "387"
47778       ],
47779       [
47780         "Botswana",
47781         "bw",
47782         "267"
47783       ],
47784       [
47785         "Brazil (Brasil)",
47786         "br",
47787         "55"
47788       ],
47789       [
47790         "British Indian Ocean Territory",
47791         "io",
47792         "246"
47793       ],
47794       [
47795         "British Virgin Islands",
47796         "vg",
47797         "1284"
47798       ],
47799       [
47800         "Brunei",
47801         "bn",
47802         "673"
47803       ],
47804       [
47805         "Bulgaria (България)",
47806         "bg",
47807         "359"
47808       ],
47809       [
47810         "Burkina Faso",
47811         "bf",
47812         "226"
47813       ],
47814       [
47815         "Burundi (Uburundi)",
47816         "bi",
47817         "257"
47818       ],
47819       [
47820         "Cambodia (កម្ពុជា)",
47821         "kh",
47822         "855"
47823       ],
47824       [
47825         "Cameroon (Cameroun)",
47826         "cm",
47827         "237"
47828       ],
47829       [
47830         "Canada",
47831         "ca",
47832         "1",
47833         1,
47834         ["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"]
47835       ],
47836       [
47837         "Cape Verde (Kabu Verdi)",
47838         "cv",
47839         "238"
47840       ],
47841       [
47842         "Caribbean Netherlands",
47843         "bq",
47844         "599",
47845         1
47846       ],
47847       [
47848         "Cayman Islands",
47849         "ky",
47850         "1345"
47851       ],
47852       [
47853         "Central African Republic (République centrafricaine)",
47854         "cf",
47855         "236"
47856       ],
47857       [
47858         "Chad (Tchad)",
47859         "td",
47860         "235"
47861       ],
47862       [
47863         "Chile",
47864         "cl",
47865         "56"
47866       ],
47867       [
47868         "China (中国)",
47869         "cn",
47870         "86"
47871       ],
47872       [
47873         "Christmas Island",
47874         "cx",
47875         "61",
47876         2
47877       ],
47878       [
47879         "Cocos (Keeling) Islands",
47880         "cc",
47881         "61",
47882         1
47883       ],
47884       [
47885         "Colombia",
47886         "co",
47887         "57"
47888       ],
47889       [
47890         "Comoros (‫جزر القمر‬‎)",
47891         "km",
47892         "269"
47893       ],
47894       [
47895         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47896         "cd",
47897         "243"
47898       ],
47899       [
47900         "Congo (Republic) (Congo-Brazzaville)",
47901         "cg",
47902         "242"
47903       ],
47904       [
47905         "Cook Islands",
47906         "ck",
47907         "682"
47908       ],
47909       [
47910         "Costa Rica",
47911         "cr",
47912         "506"
47913       ],
47914       [
47915         "Côte d’Ivoire",
47916         "ci",
47917         "225"
47918       ],
47919       [
47920         "Croatia (Hrvatska)",
47921         "hr",
47922         "385"
47923       ],
47924       [
47925         "Cuba",
47926         "cu",
47927         "53"
47928       ],
47929       [
47930         "Curaçao",
47931         "cw",
47932         "599",
47933         0
47934       ],
47935       [
47936         "Cyprus (Κύπρος)",
47937         "cy",
47938         "357"
47939       ],
47940       [
47941         "Czech Republic (Česká republika)",
47942         "cz",
47943         "420"
47944       ],
47945       [
47946         "Denmark (Danmark)",
47947         "dk",
47948         "45"
47949       ],
47950       [
47951         "Djibouti",
47952         "dj",
47953         "253"
47954       ],
47955       [
47956         "Dominica",
47957         "dm",
47958         "1767"
47959       ],
47960       [
47961         "Dominican Republic (República Dominicana)",
47962         "do",
47963         "1",
47964         2,
47965         ["809", "829", "849"]
47966       ],
47967       [
47968         "Ecuador",
47969         "ec",
47970         "593"
47971       ],
47972       [
47973         "Egypt (‫مصر‬‎)",
47974         "eg",
47975         "20"
47976       ],
47977       [
47978         "El Salvador",
47979         "sv",
47980         "503"
47981       ],
47982       [
47983         "Equatorial Guinea (Guinea Ecuatorial)",
47984         "gq",
47985         "240"
47986       ],
47987       [
47988         "Eritrea",
47989         "er",
47990         "291"
47991       ],
47992       [
47993         "Estonia (Eesti)",
47994         "ee",
47995         "372"
47996       ],
47997       [
47998         "Ethiopia",
47999         "et",
48000         "251"
48001       ],
48002       [
48003         "Falkland Islands (Islas Malvinas)",
48004         "fk",
48005         "500"
48006       ],
48007       [
48008         "Faroe Islands (Føroyar)",
48009         "fo",
48010         "298"
48011       ],
48012       [
48013         "Fiji",
48014         "fj",
48015         "679"
48016       ],
48017       [
48018         "Finland (Suomi)",
48019         "fi",
48020         "358",
48021         0
48022       ],
48023       [
48024         "France",
48025         "fr",
48026         "33"
48027       ],
48028       [
48029         "French Guiana (Guyane française)",
48030         "gf",
48031         "594"
48032       ],
48033       [
48034         "French Polynesia (Polynésie française)",
48035         "pf",
48036         "689"
48037       ],
48038       [
48039         "Gabon",
48040         "ga",
48041         "241"
48042       ],
48043       [
48044         "Gambia",
48045         "gm",
48046         "220"
48047       ],
48048       [
48049         "Georgia (საქართველო)",
48050         "ge",
48051         "995"
48052       ],
48053       [
48054         "Germany (Deutschland)",
48055         "de",
48056         "49"
48057       ],
48058       [
48059         "Ghana (Gaana)",
48060         "gh",
48061         "233"
48062       ],
48063       [
48064         "Gibraltar",
48065         "gi",
48066         "350"
48067       ],
48068       [
48069         "Greece (Ελλάδα)",
48070         "gr",
48071         "30"
48072       ],
48073       [
48074         "Greenland (Kalaallit Nunaat)",
48075         "gl",
48076         "299"
48077       ],
48078       [
48079         "Grenada",
48080         "gd",
48081         "1473"
48082       ],
48083       [
48084         "Guadeloupe",
48085         "gp",
48086         "590",
48087         0
48088       ],
48089       [
48090         "Guam",
48091         "gu",
48092         "1671"
48093       ],
48094       [
48095         "Guatemala",
48096         "gt",
48097         "502"
48098       ],
48099       [
48100         "Guernsey",
48101         "gg",
48102         "44",
48103         1
48104       ],
48105       [
48106         "Guinea (Guinée)",
48107         "gn",
48108         "224"
48109       ],
48110       [
48111         "Guinea-Bissau (Guiné Bissau)",
48112         "gw",
48113         "245"
48114       ],
48115       [
48116         "Guyana",
48117         "gy",
48118         "592"
48119       ],
48120       [
48121         "Haiti",
48122         "ht",
48123         "509"
48124       ],
48125       [
48126         "Honduras",
48127         "hn",
48128         "504"
48129       ],
48130       [
48131         "Hong Kong (香港)",
48132         "hk",
48133         "852"
48134       ],
48135       [
48136         "Hungary (Magyarország)",
48137         "hu",
48138         "36"
48139       ],
48140       [
48141         "Iceland (Ísland)",
48142         "is",
48143         "354"
48144       ],
48145       [
48146         "India (भारत)",
48147         "in",
48148         "91"
48149       ],
48150       [
48151         "Indonesia",
48152         "id",
48153         "62"
48154       ],
48155       [
48156         "Iran (‫ایران‬‎)",
48157         "ir",
48158         "98"
48159       ],
48160       [
48161         "Iraq (‫العراق‬‎)",
48162         "iq",
48163         "964"
48164       ],
48165       [
48166         "Ireland",
48167         "ie",
48168         "353"
48169       ],
48170       [
48171         "Isle of Man",
48172         "im",
48173         "44",
48174         2
48175       ],
48176       [
48177         "Israel (‫ישראל‬‎)",
48178         "il",
48179         "972"
48180       ],
48181       [
48182         "Italy (Italia)",
48183         "it",
48184         "39",
48185         0
48186       ],
48187       [
48188         "Jamaica",
48189         "jm",
48190         "1876"
48191       ],
48192       [
48193         "Japan (日本)",
48194         "jp",
48195         "81"
48196       ],
48197       [
48198         "Jersey",
48199         "je",
48200         "44",
48201         3
48202       ],
48203       [
48204         "Jordan (‫الأردن‬‎)",
48205         "jo",
48206         "962"
48207       ],
48208       [
48209         "Kazakhstan (Казахстан)",
48210         "kz",
48211         "7",
48212         1
48213       ],
48214       [
48215         "Kenya",
48216         "ke",
48217         "254"
48218       ],
48219       [
48220         "Kiribati",
48221         "ki",
48222         "686"
48223       ],
48224       [
48225         "Kosovo",
48226         "xk",
48227         "383"
48228       ],
48229       [
48230         "Kuwait (‫الكويت‬‎)",
48231         "kw",
48232         "965"
48233       ],
48234       [
48235         "Kyrgyzstan (Кыргызстан)",
48236         "kg",
48237         "996"
48238       ],
48239       [
48240         "Laos (ລາວ)",
48241         "la",
48242         "856"
48243       ],
48244       [
48245         "Latvia (Latvija)",
48246         "lv",
48247         "371"
48248       ],
48249       [
48250         "Lebanon (‫لبنان‬‎)",
48251         "lb",
48252         "961"
48253       ],
48254       [
48255         "Lesotho",
48256         "ls",
48257         "266"
48258       ],
48259       [
48260         "Liberia",
48261         "lr",
48262         "231"
48263       ],
48264       [
48265         "Libya (‫ليبيا‬‎)",
48266         "ly",
48267         "218"
48268       ],
48269       [
48270         "Liechtenstein",
48271         "li",
48272         "423"
48273       ],
48274       [
48275         "Lithuania (Lietuva)",
48276         "lt",
48277         "370"
48278       ],
48279       [
48280         "Luxembourg",
48281         "lu",
48282         "352"
48283       ],
48284       [
48285         "Macau (澳門)",
48286         "mo",
48287         "853"
48288       ],
48289       [
48290         "Macedonia (FYROM) (Македонија)",
48291         "mk",
48292         "389"
48293       ],
48294       [
48295         "Madagascar (Madagasikara)",
48296         "mg",
48297         "261"
48298       ],
48299       [
48300         "Malawi",
48301         "mw",
48302         "265"
48303       ],
48304       [
48305         "Malaysia",
48306         "my",
48307         "60"
48308       ],
48309       [
48310         "Maldives",
48311         "mv",
48312         "960"
48313       ],
48314       [
48315         "Mali",
48316         "ml",
48317         "223"
48318       ],
48319       [
48320         "Malta",
48321         "mt",
48322         "356"
48323       ],
48324       [
48325         "Marshall Islands",
48326         "mh",
48327         "692"
48328       ],
48329       [
48330         "Martinique",
48331         "mq",
48332         "596"
48333       ],
48334       [
48335         "Mauritania (‫موريتانيا‬‎)",
48336         "mr",
48337         "222"
48338       ],
48339       [
48340         "Mauritius (Moris)",
48341         "mu",
48342         "230"
48343       ],
48344       [
48345         "Mayotte",
48346         "yt",
48347         "262",
48348         1
48349       ],
48350       [
48351         "Mexico (México)",
48352         "mx",
48353         "52"
48354       ],
48355       [
48356         "Micronesia",
48357         "fm",
48358         "691"
48359       ],
48360       [
48361         "Moldova (Republica Moldova)",
48362         "md",
48363         "373"
48364       ],
48365       [
48366         "Monaco",
48367         "mc",
48368         "377"
48369       ],
48370       [
48371         "Mongolia (Монгол)",
48372         "mn",
48373         "976"
48374       ],
48375       [
48376         "Montenegro (Crna Gora)",
48377         "me",
48378         "382"
48379       ],
48380       [
48381         "Montserrat",
48382         "ms",
48383         "1664"
48384       ],
48385       [
48386         "Morocco (‫المغرب‬‎)",
48387         "ma",
48388         "212",
48389         0
48390       ],
48391       [
48392         "Mozambique (Moçambique)",
48393         "mz",
48394         "258"
48395       ],
48396       [
48397         "Myanmar (Burma) (မြန်မာ)",
48398         "mm",
48399         "95"
48400       ],
48401       [
48402         "Namibia (Namibië)",
48403         "na",
48404         "264"
48405       ],
48406       [
48407         "Nauru",
48408         "nr",
48409         "674"
48410       ],
48411       [
48412         "Nepal (नेपाल)",
48413         "np",
48414         "977"
48415       ],
48416       [
48417         "Netherlands (Nederland)",
48418         "nl",
48419         "31"
48420       ],
48421       [
48422         "New Caledonia (Nouvelle-Calédonie)",
48423         "nc",
48424         "687"
48425       ],
48426       [
48427         "New Zealand",
48428         "nz",
48429         "64"
48430       ],
48431       [
48432         "Nicaragua",
48433         "ni",
48434         "505"
48435       ],
48436       [
48437         "Niger (Nijar)",
48438         "ne",
48439         "227"
48440       ],
48441       [
48442         "Nigeria",
48443         "ng",
48444         "234"
48445       ],
48446       [
48447         "Niue",
48448         "nu",
48449         "683"
48450       ],
48451       [
48452         "Norfolk Island",
48453         "nf",
48454         "672"
48455       ],
48456       [
48457         "North Korea (조선 민주주의 인민 공화국)",
48458         "kp",
48459         "850"
48460       ],
48461       [
48462         "Northern Mariana Islands",
48463         "mp",
48464         "1670"
48465       ],
48466       [
48467         "Norway (Norge)",
48468         "no",
48469         "47",
48470         0
48471       ],
48472       [
48473         "Oman (‫عُمان‬‎)",
48474         "om",
48475         "968"
48476       ],
48477       [
48478         "Pakistan (‫پاکستان‬‎)",
48479         "pk",
48480         "92"
48481       ],
48482       [
48483         "Palau",
48484         "pw",
48485         "680"
48486       ],
48487       [
48488         "Palestine (‫فلسطين‬‎)",
48489         "ps",
48490         "970"
48491       ],
48492       [
48493         "Panama (Panamá)",
48494         "pa",
48495         "507"
48496       ],
48497       [
48498         "Papua New Guinea",
48499         "pg",
48500         "675"
48501       ],
48502       [
48503         "Paraguay",
48504         "py",
48505         "595"
48506       ],
48507       [
48508         "Peru (Perú)",
48509         "pe",
48510         "51"
48511       ],
48512       [
48513         "Philippines",
48514         "ph",
48515         "63"
48516       ],
48517       [
48518         "Poland (Polska)",
48519         "pl",
48520         "48"
48521       ],
48522       [
48523         "Portugal",
48524         "pt",
48525         "351"
48526       ],
48527       [
48528         "Puerto Rico",
48529         "pr",
48530         "1",
48531         3,
48532         ["787", "939"]
48533       ],
48534       [
48535         "Qatar (‫قطر‬‎)",
48536         "qa",
48537         "974"
48538       ],
48539       [
48540         "Réunion (La Réunion)",
48541         "re",
48542         "262",
48543         0
48544       ],
48545       [
48546         "Romania (România)",
48547         "ro",
48548         "40"
48549       ],
48550       [
48551         "Russia (Россия)",
48552         "ru",
48553         "7",
48554         0
48555       ],
48556       [
48557         "Rwanda",
48558         "rw",
48559         "250"
48560       ],
48561       [
48562         "Saint Barthélemy",
48563         "bl",
48564         "590",
48565         1
48566       ],
48567       [
48568         "Saint Helena",
48569         "sh",
48570         "290"
48571       ],
48572       [
48573         "Saint Kitts and Nevis",
48574         "kn",
48575         "1869"
48576       ],
48577       [
48578         "Saint Lucia",
48579         "lc",
48580         "1758"
48581       ],
48582       [
48583         "Saint Martin (Saint-Martin (partie française))",
48584         "mf",
48585         "590",
48586         2
48587       ],
48588       [
48589         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48590         "pm",
48591         "508"
48592       ],
48593       [
48594         "Saint Vincent and the Grenadines",
48595         "vc",
48596         "1784"
48597       ],
48598       [
48599         "Samoa",
48600         "ws",
48601         "685"
48602       ],
48603       [
48604         "San Marino",
48605         "sm",
48606         "378"
48607       ],
48608       [
48609         "São Tomé and Príncipe (São Tomé e Príncipe)",
48610         "st",
48611         "239"
48612       ],
48613       [
48614         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
48615         "sa",
48616         "966"
48617       ],
48618       [
48619         "Senegal (Sénégal)",
48620         "sn",
48621         "221"
48622       ],
48623       [
48624         "Serbia (Србија)",
48625         "rs",
48626         "381"
48627       ],
48628       [
48629         "Seychelles",
48630         "sc",
48631         "248"
48632       ],
48633       [
48634         "Sierra Leone",
48635         "sl",
48636         "232"
48637       ],
48638       [
48639         "Singapore",
48640         "sg",
48641         "65"
48642       ],
48643       [
48644         "Sint Maarten",
48645         "sx",
48646         "1721"
48647       ],
48648       [
48649         "Slovakia (Slovensko)",
48650         "sk",
48651         "421"
48652       ],
48653       [
48654         "Slovenia (Slovenija)",
48655         "si",
48656         "386"
48657       ],
48658       [
48659         "Solomon Islands",
48660         "sb",
48661         "677"
48662       ],
48663       [
48664         "Somalia (Soomaaliya)",
48665         "so",
48666         "252"
48667       ],
48668       [
48669         "South Africa",
48670         "za",
48671         "27"
48672       ],
48673       [
48674         "South Korea (대한민국)",
48675         "kr",
48676         "82"
48677       ],
48678       [
48679         "South Sudan (‫جنوب السودان‬‎)",
48680         "ss",
48681         "211"
48682       ],
48683       [
48684         "Spain (España)",
48685         "es",
48686         "34"
48687       ],
48688       [
48689         "Sri Lanka (ශ්‍රී ලංකාව)",
48690         "lk",
48691         "94"
48692       ],
48693       [
48694         "Sudan (‫السودان‬‎)",
48695         "sd",
48696         "249"
48697       ],
48698       [
48699         "Suriname",
48700         "sr",
48701         "597"
48702       ],
48703       [
48704         "Svalbard and Jan Mayen",
48705         "sj",
48706         "47",
48707         1
48708       ],
48709       [
48710         "Swaziland",
48711         "sz",
48712         "268"
48713       ],
48714       [
48715         "Sweden (Sverige)",
48716         "se",
48717         "46"
48718       ],
48719       [
48720         "Switzerland (Schweiz)",
48721         "ch",
48722         "41"
48723       ],
48724       [
48725         "Syria (‫سوريا‬‎)",
48726         "sy",
48727         "963"
48728       ],
48729       [
48730         "Taiwan (台灣)",
48731         "tw",
48732         "886"
48733       ],
48734       [
48735         "Tajikistan",
48736         "tj",
48737         "992"
48738       ],
48739       [
48740         "Tanzania",
48741         "tz",
48742         "255"
48743       ],
48744       [
48745         "Thailand (ไทย)",
48746         "th",
48747         "66"
48748       ],
48749       [
48750         "Timor-Leste",
48751         "tl",
48752         "670"
48753       ],
48754       [
48755         "Togo",
48756         "tg",
48757         "228"
48758       ],
48759       [
48760         "Tokelau",
48761         "tk",
48762         "690"
48763       ],
48764       [
48765         "Tonga",
48766         "to",
48767         "676"
48768       ],
48769       [
48770         "Trinidad and Tobago",
48771         "tt",
48772         "1868"
48773       ],
48774       [
48775         "Tunisia (‫تونس‬‎)",
48776         "tn",
48777         "216"
48778       ],
48779       [
48780         "Turkey (Türkiye)",
48781         "tr",
48782         "90"
48783       ],
48784       [
48785         "Turkmenistan",
48786         "tm",
48787         "993"
48788       ],
48789       [
48790         "Turks and Caicos Islands",
48791         "tc",
48792         "1649"
48793       ],
48794       [
48795         "Tuvalu",
48796         "tv",
48797         "688"
48798       ],
48799       [
48800         "U.S. Virgin Islands",
48801         "vi",
48802         "1340"
48803       ],
48804       [
48805         "Uganda",
48806         "ug",
48807         "256"
48808       ],
48809       [
48810         "Ukraine (Україна)",
48811         "ua",
48812         "380"
48813       ],
48814       [
48815         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
48816         "ae",
48817         "971"
48818       ],
48819       [
48820         "United Kingdom",
48821         "gb",
48822         "44",
48823         0
48824       ],
48825       [
48826         "United States",
48827         "us",
48828         "1",
48829         0
48830       ],
48831       [
48832         "Uruguay",
48833         "uy",
48834         "598"
48835       ],
48836       [
48837         "Uzbekistan (Oʻzbekiston)",
48838         "uz",
48839         "998"
48840       ],
48841       [
48842         "Vanuatu",
48843         "vu",
48844         "678"
48845       ],
48846       [
48847         "Vatican City (Città del Vaticano)",
48848         "va",
48849         "39",
48850         1
48851       ],
48852       [
48853         "Venezuela",
48854         "ve",
48855         "58"
48856       ],
48857       [
48858         "Vietnam (Việt Nam)",
48859         "vn",
48860         "84"
48861       ],
48862       [
48863         "Wallis and Futuna (Wallis-et-Futuna)",
48864         "wf",
48865         "681"
48866       ],
48867       [
48868         "Western Sahara (‫الصحراء الغربية‬‎)",
48869         "eh",
48870         "212",
48871         1
48872       ],
48873       [
48874         "Yemen (‫اليمن‬‎)",
48875         "ye",
48876         "967"
48877       ],
48878       [
48879         "Zambia",
48880         "zm",
48881         "260"
48882       ],
48883       [
48884         "Zimbabwe",
48885         "zw",
48886         "263"
48887       ],
48888       [
48889         "Åland Islands",
48890         "ax",
48891         "358",
48892         1
48893       ]
48894   ];
48895   
48896   return d;
48897 }/**
48898 *    This script refer to:
48899 *    Title: International Telephone Input
48900 *    Author: Jack O'Connor
48901 *    Code version:  v12.1.12
48902 *    Availability: https://github.com/jackocnr/intl-tel-input.git
48903 **/
48904
48905 /**
48906  * @class Roo.bootstrap.form.PhoneInput
48907  * @extends Roo.bootstrap.form.TriggerField
48908  * An input with International dial-code selection
48909  
48910  * @cfg {String} defaultDialCode default '+852'
48911  * @cfg {Array} preferedCountries default []
48912   
48913  * @constructor
48914  * Create a new PhoneInput.
48915  * @param {Object} config Configuration options
48916  */
48917
48918 Roo.bootstrap.form.PhoneInput = function(config) {
48919     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48920 };
48921
48922 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48923         /**
48924         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48925         */
48926         listWidth: undefined,
48927         
48928         selectedClass: 'active',
48929         
48930         invalidClass : "has-warning",
48931         
48932         validClass: 'has-success',
48933         
48934         allowed: '0123456789',
48935         
48936         max_length: 15,
48937         
48938         /**
48939          * @cfg {String} defaultDialCode The default dial code when initializing the input
48940          */
48941         defaultDialCode: '+852',
48942         
48943         /**
48944          * @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
48945          */
48946         preferedCountries: false,
48947         
48948         getAutoCreate : function()
48949         {
48950             var data = Roo.bootstrap.form.PhoneInputData();
48951             var align = this.labelAlign || this.parentLabelAlign();
48952             var id = Roo.id();
48953             
48954             this.allCountries = [];
48955             this.dialCodeMapping = [];
48956             
48957             for (var i = 0; i < data.length; i++) {
48958               var c = data[i];
48959               this.allCountries[i] = {
48960                 name: c[0],
48961                 iso2: c[1],
48962                 dialCode: c[2],
48963                 priority: c[3] || 0,
48964                 areaCodes: c[4] || null
48965               };
48966               this.dialCodeMapping[c[2]] = {
48967                   name: c[0],
48968                   iso2: c[1],
48969                   priority: c[3] || 0,
48970                   areaCodes: c[4] || null
48971               };
48972             }
48973             
48974             var cfg = {
48975                 cls: 'form-group',
48976                 cn: []
48977             };
48978             
48979             var input =  {
48980                 tag: 'input',
48981                 id : id,
48982                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48983                 maxlength: this.max_length,
48984                 cls : 'form-control tel-input',
48985                 autocomplete: 'new-password'
48986             };
48987             
48988             var hiddenInput = {
48989                 tag: 'input',
48990                 type: 'hidden',
48991                 cls: 'hidden-tel-input'
48992             };
48993             
48994             if (this.name) {
48995                 hiddenInput.name = this.name;
48996             }
48997             
48998             if (this.disabled) {
48999                 input.disabled = true;
49000             }
49001             
49002             var flag_container = {
49003                 tag: 'div',
49004                 cls: 'flag-box',
49005                 cn: [
49006                     {
49007                         tag: 'div',
49008                         cls: 'flag'
49009                     },
49010                     {
49011                         tag: 'div',
49012                         cls: 'caret'
49013                     }
49014                 ]
49015             };
49016             
49017             var box = {
49018                 tag: 'div',
49019                 cls: this.hasFeedback ? 'has-feedback' : '',
49020                 cn: [
49021                     hiddenInput,
49022                     input,
49023                     {
49024                         tag: 'input',
49025                         cls: 'dial-code-holder',
49026                         disabled: true
49027                     }
49028                 ]
49029             };
49030             
49031             var container = {
49032                 cls: 'roo-select2-container input-group',
49033                 cn: [
49034                     flag_container,
49035                     box
49036                 ]
49037             };
49038             
49039             if (this.fieldLabel.length) {
49040                 var indicator = {
49041                     tag: 'i',
49042                     tooltip: 'This field is required'
49043                 };
49044                 
49045                 var label = {
49046                     tag: 'label',
49047                     'for':  id,
49048                     cls: 'control-label',
49049                     cn: []
49050                 };
49051                 
49052                 var label_text = {
49053                     tag: 'span',
49054                     html: this.fieldLabel
49055                 };
49056                 
49057                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49058                 label.cn = [
49059                     indicator,
49060                     label_text
49061                 ];
49062                 
49063                 if(this.indicatorpos == 'right') {
49064                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49065                     label.cn = [
49066                         label_text,
49067                         indicator
49068                     ];
49069                 }
49070                 
49071                 if(align == 'left') {
49072                     container = {
49073                         tag: 'div',
49074                         cn: [
49075                             container
49076                         ]
49077                     };
49078                     
49079                     if(this.labelWidth > 12){
49080                         label.style = "width: " + this.labelWidth + 'px';
49081                     }
49082                     if(this.labelWidth < 13 && this.labelmd == 0){
49083                         this.labelmd = this.labelWidth;
49084                     }
49085                     if(this.labellg > 0){
49086                         label.cls += ' col-lg-' + this.labellg;
49087                         input.cls += ' col-lg-' + (12 - this.labellg);
49088                     }
49089                     if(this.labelmd > 0){
49090                         label.cls += ' col-md-' + this.labelmd;
49091                         container.cls += ' col-md-' + (12 - this.labelmd);
49092                     }
49093                     if(this.labelsm > 0){
49094                         label.cls += ' col-sm-' + this.labelsm;
49095                         container.cls += ' col-sm-' + (12 - this.labelsm);
49096                     }
49097                     if(this.labelxs > 0){
49098                         label.cls += ' col-xs-' + this.labelxs;
49099                         container.cls += ' col-xs-' + (12 - this.labelxs);
49100                     }
49101                 }
49102             }
49103             
49104             cfg.cn = [
49105                 label,
49106                 container
49107             ];
49108             
49109             var settings = this;
49110             
49111             ['xs','sm','md','lg'].map(function(size){
49112                 if (settings[size]) {
49113                     cfg.cls += ' col-' + size + '-' + settings[size];
49114                 }
49115             });
49116             
49117             this.store = new Roo.data.Store({
49118                 proxy : new Roo.data.MemoryProxy({}),
49119                 reader : new Roo.data.JsonReader({
49120                     fields : [
49121                         {
49122                             'name' : 'name',
49123                             'type' : 'string'
49124                         },
49125                         {
49126                             'name' : 'iso2',
49127                             'type' : 'string'
49128                         },
49129                         {
49130                             'name' : 'dialCode',
49131                             'type' : 'string'
49132                         },
49133                         {
49134                             'name' : 'priority',
49135                             'type' : 'string'
49136                         },
49137                         {
49138                             'name' : 'areaCodes',
49139                             'type' : 'string'
49140                         }
49141                     ]
49142                 })
49143             });
49144             
49145             if(!this.preferedCountries) {
49146                 this.preferedCountries = [
49147                     'hk',
49148                     'gb',
49149                     'us'
49150                 ];
49151             }
49152             
49153             var p = this.preferedCountries.reverse();
49154             
49155             if(p) {
49156                 for (var i = 0; i < p.length; i++) {
49157                     for (var j = 0; j < this.allCountries.length; j++) {
49158                         if(this.allCountries[j].iso2 == p[i]) {
49159                             var t = this.allCountries[j];
49160                             this.allCountries.splice(j,1);
49161                             this.allCountries.unshift(t);
49162                         }
49163                     } 
49164                 }
49165             }
49166             
49167             this.store.proxy.data = {
49168                 success: true,
49169                 data: this.allCountries
49170             };
49171             
49172             return cfg;
49173         },
49174         
49175         initEvents : function()
49176         {
49177             this.createList();
49178             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
49179             
49180             this.indicator = this.indicatorEl();
49181             this.flag = this.flagEl();
49182             this.dialCodeHolder = this.dialCodeHolderEl();
49183             
49184             this.trigger = this.el.select('div.flag-box',true).first();
49185             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
49186             
49187             var _this = this;
49188             
49189             (function(){
49190                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49191                 _this.list.setWidth(lw);
49192             }).defer(100);
49193             
49194             this.list.on('mouseover', this.onViewOver, this);
49195             this.list.on('mousemove', this.onViewMove, this);
49196             this.inputEl().on("keyup", this.onKeyUp, this);
49197             this.inputEl().on("keypress", this.onKeyPress, this);
49198             
49199             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
49200
49201             this.view = new Roo.View(this.list, this.tpl, {
49202                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
49203             });
49204             
49205             this.view.on('click', this.onViewClick, this);
49206             this.setValue(this.defaultDialCode);
49207         },
49208         
49209         onTriggerClick : function(e)
49210         {
49211             Roo.log('trigger click');
49212             if(this.disabled){
49213                 return;
49214             }
49215             
49216             if(this.isExpanded()){
49217                 this.collapse();
49218                 this.hasFocus = false;
49219             }else {
49220                 this.store.load({});
49221                 this.hasFocus = true;
49222                 this.expand();
49223             }
49224         },
49225         
49226         isExpanded : function()
49227         {
49228             return this.list.isVisible();
49229         },
49230         
49231         collapse : function()
49232         {
49233             if(!this.isExpanded()){
49234                 return;
49235             }
49236             this.list.hide();
49237             Roo.get(document).un('mousedown', this.collapseIf, this);
49238             Roo.get(document).un('mousewheel', this.collapseIf, this);
49239             this.fireEvent('collapse', this);
49240             this.validate();
49241         },
49242         
49243         expand : function()
49244         {
49245             Roo.log('expand');
49246
49247             if(this.isExpanded() || !this.hasFocus){
49248                 return;
49249             }
49250             
49251             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
49252             this.list.setWidth(lw);
49253             
49254             this.list.show();
49255             this.restrictHeight();
49256             
49257             Roo.get(document).on('mousedown', this.collapseIf, this);
49258             Roo.get(document).on('mousewheel', this.collapseIf, this);
49259             
49260             this.fireEvent('expand', this);
49261         },
49262         
49263         restrictHeight : function()
49264         {
49265             this.list.alignTo(this.inputEl(), this.listAlign);
49266             this.list.alignTo(this.inputEl(), this.listAlign);
49267         },
49268         
49269         onViewOver : function(e, t)
49270         {
49271             if(this.inKeyMode){
49272                 return;
49273             }
49274             var item = this.view.findItemFromChild(t);
49275             
49276             if(item){
49277                 var index = this.view.indexOf(item);
49278                 this.select(index, false);
49279             }
49280         },
49281
49282         // private
49283         onViewClick : function(view, doFocus, el, e)
49284         {
49285             var index = this.view.getSelectedIndexes()[0];
49286             
49287             var r = this.store.getAt(index);
49288             
49289             if(r){
49290                 this.onSelect(r, index);
49291             }
49292             if(doFocus !== false && !this.blockFocus){
49293                 this.inputEl().focus();
49294             }
49295         },
49296         
49297         onViewMove : function(e, t)
49298         {
49299             this.inKeyMode = false;
49300         },
49301         
49302         select : function(index, scrollIntoView)
49303         {
49304             this.selectedIndex = index;
49305             this.view.select(index);
49306             if(scrollIntoView !== false){
49307                 var el = this.view.getNode(index);
49308                 if(el){
49309                     this.list.scrollChildIntoView(el, false);
49310                 }
49311             }
49312         },
49313         
49314         createList : function()
49315         {
49316             this.list = Roo.get(document.body).createChild({
49317                 tag: 'ul',
49318                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
49319                 style: 'display:none'
49320             });
49321             
49322             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
49323         },
49324         
49325         collapseIf : function(e)
49326         {
49327             var in_combo  = e.within(this.el);
49328             var in_list =  e.within(this.list);
49329             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
49330             
49331             if (in_combo || in_list || is_list) {
49332                 return;
49333             }
49334             this.collapse();
49335         },
49336         
49337         onSelect : function(record, index)
49338         {
49339             if(this.fireEvent('beforeselect', this, record, index) !== false){
49340                 
49341                 this.setFlagClass(record.data.iso2);
49342                 this.setDialCode(record.data.dialCode);
49343                 this.hasFocus = false;
49344                 this.collapse();
49345                 this.fireEvent('select', this, record, index);
49346             }
49347         },
49348         
49349         flagEl : function()
49350         {
49351             var flag = this.el.select('div.flag',true).first();
49352             if(!flag){
49353                 return false;
49354             }
49355             return flag;
49356         },
49357         
49358         dialCodeHolderEl : function()
49359         {
49360             var d = this.el.select('input.dial-code-holder',true).first();
49361             if(!d){
49362                 return false;
49363             }
49364             return d;
49365         },
49366         
49367         setDialCode : function(v)
49368         {
49369             this.dialCodeHolder.dom.value = '+'+v;
49370         },
49371         
49372         setFlagClass : function(n)
49373         {
49374             this.flag.dom.className = 'flag '+n;
49375         },
49376         
49377         getValue : function()
49378         {
49379             var v = this.inputEl().getValue();
49380             if(this.dialCodeHolder) {
49381                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
49382             }
49383             return v;
49384         },
49385         
49386         setValue : function(v)
49387         {
49388             var d = this.getDialCode(v);
49389             
49390             //invalid dial code
49391             if(v.length == 0 || !d || d.length == 0) {
49392                 if(this.rendered){
49393                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
49394                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49395                 }
49396                 return;
49397             }
49398             
49399             //valid dial code
49400             this.setFlagClass(this.dialCodeMapping[d].iso2);
49401             this.setDialCode(d);
49402             this.inputEl().dom.value = v.replace('+'+d,'');
49403             this.hiddenEl().dom.value = this.getValue();
49404             
49405             this.validate();
49406         },
49407         
49408         getDialCode : function(v)
49409         {
49410             v = v ||  '';
49411             
49412             if (v.length == 0) {
49413                 return this.dialCodeHolder.dom.value;
49414             }
49415             
49416             var dialCode = "";
49417             if (v.charAt(0) != "+") {
49418                 return false;
49419             }
49420             var numericChars = "";
49421             for (var i = 1; i < v.length; i++) {
49422               var c = v.charAt(i);
49423               if (!isNaN(c)) {
49424                 numericChars += c;
49425                 if (this.dialCodeMapping[numericChars]) {
49426                   dialCode = v.substr(1, i);
49427                 }
49428                 if (numericChars.length == 4) {
49429                   break;
49430                 }
49431               }
49432             }
49433             return dialCode;
49434         },
49435         
49436         reset : function()
49437         {
49438             this.setValue(this.defaultDialCode);
49439             this.validate();
49440         },
49441         
49442         hiddenEl : function()
49443         {
49444             return this.el.select('input.hidden-tel-input',true).first();
49445         },
49446         
49447         // after setting val
49448         onKeyUp : function(e){
49449             this.setValue(this.getValue());
49450         },
49451         
49452         onKeyPress : function(e){
49453             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
49454                 e.stopEvent();
49455             }
49456         }
49457         
49458 });
49459 /**
49460  * @class Roo.bootstrap.form.MoneyField
49461  * @extends Roo.bootstrap.form.ComboBox
49462  * Bootstrap MoneyField class
49463  * 
49464  * @constructor
49465  * Create a new MoneyField.
49466  * @param {Object} config Configuration options
49467  */
49468
49469 Roo.bootstrap.form.MoneyField = function(config) {
49470     
49471     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
49472     
49473 };
49474
49475 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
49476     
49477     /**
49478      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
49479      */
49480     allowDecimals : true,
49481     /**
49482      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
49483      */
49484     decimalSeparator : ".",
49485     /**
49486      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
49487      */
49488     decimalPrecision : 0,
49489     /**
49490      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
49491      */
49492     allowNegative : true,
49493     /**
49494      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
49495      */
49496     allowZero: true,
49497     /**
49498      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
49499      */
49500     minValue : Number.NEGATIVE_INFINITY,
49501     /**
49502      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
49503      */
49504     maxValue : Number.MAX_VALUE,
49505     /**
49506      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
49507      */
49508     minText : "The minimum value for this field is {0}",
49509     /**
49510      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
49511      */
49512     maxText : "The maximum value for this field is {0}",
49513     /**
49514      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
49515      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
49516      */
49517     nanText : "{0} is not a valid number",
49518     /**
49519      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
49520      */
49521     castInt : true,
49522     /**
49523      * @cfg {String} defaults currency of the MoneyField
49524      * value should be in lkey
49525      */
49526     defaultCurrency : false,
49527     /**
49528      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
49529      */
49530     thousandsDelimiter : false,
49531     /**
49532      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
49533      */
49534     max_length: false,
49535     
49536     inputlg : 9,
49537     inputmd : 9,
49538     inputsm : 9,
49539     inputxs : 6,
49540      /**
49541      * @cfg {Roo.data.Store} store  Store to lookup currency??
49542      */
49543     store : false,
49544     
49545     getAutoCreate : function()
49546     {
49547         var align = this.labelAlign || this.parentLabelAlign();
49548         
49549         var id = Roo.id();
49550
49551         var cfg = {
49552             cls: 'form-group',
49553             cn: []
49554         };
49555
49556         var input =  {
49557             tag: 'input',
49558             id : id,
49559             cls : 'form-control roo-money-amount-input',
49560             autocomplete: 'new-password'
49561         };
49562         
49563         var hiddenInput = {
49564             tag: 'input',
49565             type: 'hidden',
49566             id: Roo.id(),
49567             cls: 'hidden-number-input'
49568         };
49569         
49570         if(this.max_length) {
49571             input.maxlength = this.max_length; 
49572         }
49573         
49574         if (this.name) {
49575             hiddenInput.name = this.name;
49576         }
49577
49578         if (this.disabled) {
49579             input.disabled = true;
49580         }
49581
49582         var clg = 12 - this.inputlg;
49583         var cmd = 12 - this.inputmd;
49584         var csm = 12 - this.inputsm;
49585         var cxs = 12 - this.inputxs;
49586         
49587         var container = {
49588             tag : 'div',
49589             cls : 'row roo-money-field',
49590             cn : [
49591                 {
49592                     tag : 'div',
49593                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49594                     cn : [
49595                         {
49596                             tag : 'div',
49597                             cls: 'roo-select2-container input-group',
49598                             cn: [
49599                                 {
49600                                     tag : 'input',
49601                                     cls : 'form-control roo-money-currency-input',
49602                                     autocomplete: 'new-password',
49603                                     readOnly : 1,
49604                                     name : this.currencyName
49605                                 },
49606                                 {
49607                                     tag :'span',
49608                                     cls : 'input-group-addon',
49609                                     cn : [
49610                                         {
49611                                             tag: 'span',
49612                                             cls: 'caret'
49613                                         }
49614                                     ]
49615                                 }
49616                             ]
49617                         }
49618                     ]
49619                 },
49620                 {
49621                     tag : 'div',
49622                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49623                     cn : [
49624                         {
49625                             tag: 'div',
49626                             cls: this.hasFeedback ? 'has-feedback' : '',
49627                             cn: [
49628                                 input
49629                             ]
49630                         }
49631                     ]
49632                 }
49633             ]
49634             
49635         };
49636         
49637         if (this.fieldLabel.length) {
49638             var indicator = {
49639                 tag: 'i',
49640                 tooltip: 'This field is required'
49641             };
49642
49643             var label = {
49644                 tag: 'label',
49645                 'for':  id,
49646                 cls: 'control-label',
49647                 cn: []
49648             };
49649
49650             var label_text = {
49651                 tag: 'span',
49652                 html: this.fieldLabel
49653             };
49654
49655             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49656             label.cn = [
49657                 indicator,
49658                 label_text
49659             ];
49660
49661             if(this.indicatorpos == 'right') {
49662                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49663                 label.cn = [
49664                     label_text,
49665                     indicator
49666                 ];
49667             }
49668
49669             if(align == 'left') {
49670                 container = {
49671                     tag: 'div',
49672                     cn: [
49673                         container
49674                     ]
49675                 };
49676
49677                 if(this.labelWidth > 12){
49678                     label.style = "width: " + this.labelWidth + 'px';
49679                 }
49680                 if(this.labelWidth < 13 && this.labelmd == 0){
49681                     this.labelmd = this.labelWidth;
49682                 }
49683                 if(this.labellg > 0){
49684                     label.cls += ' col-lg-' + this.labellg;
49685                     input.cls += ' col-lg-' + (12 - this.labellg);
49686                 }
49687                 if(this.labelmd > 0){
49688                     label.cls += ' col-md-' + this.labelmd;
49689                     container.cls += ' col-md-' + (12 - this.labelmd);
49690                 }
49691                 if(this.labelsm > 0){
49692                     label.cls += ' col-sm-' + this.labelsm;
49693                     container.cls += ' col-sm-' + (12 - this.labelsm);
49694                 }
49695                 if(this.labelxs > 0){
49696                     label.cls += ' col-xs-' + this.labelxs;
49697                     container.cls += ' col-xs-' + (12 - this.labelxs);
49698                 }
49699             }
49700         }
49701
49702         cfg.cn = [
49703             label,
49704             container,
49705             hiddenInput
49706         ];
49707         
49708         var settings = this;
49709
49710         ['xs','sm','md','lg'].map(function(size){
49711             if (settings[size]) {
49712                 cfg.cls += ' col-' + size + '-' + settings[size];
49713             }
49714         });
49715         
49716         return cfg;
49717     },
49718     
49719     initEvents : function()
49720     {
49721         this.indicator = this.indicatorEl();
49722         
49723         this.initCurrencyEvent();
49724         
49725         this.initNumberEvent();
49726     },
49727     
49728     initCurrencyEvent : function()
49729     {
49730         if (!this.store) {
49731             throw "can not find store for combo";
49732         }
49733         
49734         this.store = Roo.factory(this.store, Roo.data);
49735         this.store.parent = this;
49736         
49737         this.createList();
49738         
49739         this.triggerEl = this.el.select('.input-group-addon', true).first();
49740         
49741         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49742         
49743         var _this = this;
49744         
49745         (function(){
49746             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49747             _this.list.setWidth(lw);
49748         }).defer(100);
49749         
49750         this.list.on('mouseover', this.onViewOver, this);
49751         this.list.on('mousemove', this.onViewMove, this);
49752         this.list.on('scroll', this.onViewScroll, this);
49753         
49754         if(!this.tpl){
49755             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49756         }
49757         
49758         this.view = new Roo.View(this.list, this.tpl, {
49759             singleSelect:true, store: this.store, selectedClass: this.selectedClass
49760         });
49761         
49762         this.view.on('click', this.onViewClick, this);
49763         
49764         this.store.on('beforeload', this.onBeforeLoad, this);
49765         this.store.on('load', this.onLoad, this);
49766         this.store.on('loadexception', this.onLoadException, this);
49767         
49768         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49769             "up" : function(e){
49770                 this.inKeyMode = true;
49771                 this.selectPrev();
49772             },
49773
49774             "down" : function(e){
49775                 if(!this.isExpanded()){
49776                     this.onTriggerClick();
49777                 }else{
49778                     this.inKeyMode = true;
49779                     this.selectNext();
49780                 }
49781             },
49782
49783             "enter" : function(e){
49784                 this.collapse();
49785                 
49786                 if(this.fireEvent("specialkey", this, e)){
49787                     this.onViewClick(false);
49788                 }
49789                 
49790                 return true;
49791             },
49792
49793             "esc" : function(e){
49794                 this.collapse();
49795             },
49796
49797             "tab" : function(e){
49798                 this.collapse();
49799                 
49800                 if(this.fireEvent("specialkey", this, e)){
49801                     this.onViewClick(false);
49802                 }
49803                 
49804                 return true;
49805             },
49806
49807             scope : this,
49808
49809             doRelay : function(foo, bar, hname){
49810                 if(hname == 'down' || this.scope.isExpanded()){
49811                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49812                 }
49813                 return true;
49814             },
49815
49816             forceKeyDown: true
49817         });
49818         
49819         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49820         
49821     },
49822     
49823     initNumberEvent : function(e)
49824     {
49825         this.inputEl().on("keydown" , this.fireKey,  this);
49826         this.inputEl().on("focus", this.onFocus,  this);
49827         this.inputEl().on("blur", this.onBlur,  this);
49828         
49829         this.inputEl().relayEvent('keyup', this);
49830         
49831         if(this.indicator){
49832             this.indicator.addClass('invisible');
49833         }
49834  
49835         this.originalValue = this.getValue();
49836         
49837         if(this.validationEvent == 'keyup'){
49838             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49839             this.inputEl().on('keyup', this.filterValidation, this);
49840         }
49841         else if(this.validationEvent !== false){
49842             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49843         }
49844         
49845         if(this.selectOnFocus){
49846             this.on("focus", this.preFocus, this);
49847             
49848         }
49849         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49850             this.inputEl().on("keypress", this.filterKeys, this);
49851         } else {
49852             this.inputEl().relayEvent('keypress', this);
49853         }
49854         
49855         var allowed = "0123456789";
49856         
49857         if(this.allowDecimals){
49858             allowed += this.decimalSeparator;
49859         }
49860         
49861         if(this.allowNegative){
49862             allowed += "-";
49863         }
49864         
49865         if(this.thousandsDelimiter) {
49866             allowed += ",";
49867         }
49868         
49869         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49870         
49871         var keyPress = function(e){
49872             
49873             var k = e.getKey();
49874             
49875             var c = e.getCharCode();
49876             
49877             if(
49878                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49879                     allowed.indexOf(String.fromCharCode(c)) === -1
49880             ){
49881                 e.stopEvent();
49882                 return;
49883             }
49884             
49885             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49886                 return;
49887             }
49888             
49889             if(allowed.indexOf(String.fromCharCode(c)) === -1){
49890                 e.stopEvent();
49891             }
49892         };
49893         
49894         this.inputEl().on("keypress", keyPress, this);
49895         
49896     },
49897     
49898     onTriggerClick : function(e)
49899     {   
49900         if(this.disabled){
49901             return;
49902         }
49903         
49904         this.page = 0;
49905         this.loadNext = false;
49906         
49907         if(this.isExpanded()){
49908             this.collapse();
49909             return;
49910         }
49911         
49912         this.hasFocus = true;
49913         
49914         if(this.triggerAction == 'all') {
49915             this.doQuery(this.allQuery, true);
49916             return;
49917         }
49918         
49919         this.doQuery(this.getRawValue());
49920     },
49921     
49922     getCurrency : function()
49923     {   
49924         var v = this.currencyEl().getValue();
49925         
49926         return v;
49927     },
49928     
49929     restrictHeight : function()
49930     {
49931         this.list.alignTo(this.currencyEl(), this.listAlign);
49932         this.list.alignTo(this.currencyEl(), this.listAlign);
49933     },
49934     
49935     onViewClick : function(view, doFocus, el, e)
49936     {
49937         var index = this.view.getSelectedIndexes()[0];
49938         
49939         var r = this.store.getAt(index);
49940         
49941         if(r){
49942             this.onSelect(r, index);
49943         }
49944     },
49945     
49946     onSelect : function(record, index){
49947         
49948         if(this.fireEvent('beforeselect', this, record, index) !== false){
49949         
49950             this.setFromCurrencyData(index > -1 ? record.data : false);
49951             
49952             this.collapse();
49953             
49954             this.fireEvent('select', this, record, index);
49955         }
49956     },
49957     
49958     setFromCurrencyData : function(o)
49959     {
49960         var currency = '';
49961         
49962         this.lastCurrency = o;
49963         
49964         if (this.currencyField) {
49965             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49966         } else {
49967             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49968         }
49969         
49970         this.lastSelectionText = currency;
49971         
49972         //setting default currency
49973         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49974             this.setCurrency(this.defaultCurrency);
49975             return;
49976         }
49977         
49978         this.setCurrency(currency);
49979     },
49980     
49981     setFromData : function(o)
49982     {
49983         var c = {};
49984         
49985         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49986         
49987         this.setFromCurrencyData(c);
49988         
49989         var value = '';
49990         
49991         if (this.name) {
49992             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49993         } else {
49994             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49995         }
49996         
49997         this.setValue(value);
49998         
49999     },
50000     
50001     setCurrency : function(v)
50002     {   
50003         this.currencyValue = v;
50004         
50005         if(this.rendered){
50006             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
50007             this.validate();
50008         }
50009     },
50010     
50011     setValue : function(v)
50012     {
50013         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
50014         
50015         this.value = v;
50016         
50017         if(this.rendered){
50018             
50019             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
50020             
50021             this.inputEl().dom.value = (v == '') ? '' :
50022                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
50023             
50024             if(!this.allowZero && v === '0') {
50025                 this.hiddenEl().dom.value = '';
50026                 this.inputEl().dom.value = '';
50027             }
50028             
50029             this.validate();
50030         }
50031     },
50032     
50033     getRawValue : function()
50034     {
50035         var v = this.inputEl().getValue();
50036         
50037         return v;
50038     },
50039     
50040     getValue : function()
50041     {
50042         return this.fixPrecision(this.parseValue(this.getRawValue()));
50043     },
50044     
50045     parseValue : function(value)
50046     {
50047         if(this.thousandsDelimiter) {
50048             value += "";
50049             r = new RegExp(",", "g");
50050             value = value.replace(r, "");
50051         }
50052         
50053         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
50054         return isNaN(value) ? '' : value;
50055         
50056     },
50057     
50058     fixPrecision : function(value)
50059     {
50060         if(this.thousandsDelimiter) {
50061             value += "";
50062             r = new RegExp(",", "g");
50063             value = value.replace(r, "");
50064         }
50065         
50066         var nan = isNaN(value);
50067         
50068         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
50069             return nan ? '' : value;
50070         }
50071         return parseFloat(value).toFixed(this.decimalPrecision);
50072     },
50073     
50074     decimalPrecisionFcn : function(v)
50075     {
50076         return Math.floor(v);
50077     },
50078     
50079     validateValue : function(value)
50080     {
50081         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
50082             return false;
50083         }
50084         
50085         var num = this.parseValue(value);
50086         
50087         if(isNaN(num)){
50088             this.markInvalid(String.format(this.nanText, value));
50089             return false;
50090         }
50091         
50092         if(num < this.minValue){
50093             this.markInvalid(String.format(this.minText, this.minValue));
50094             return false;
50095         }
50096         
50097         if(num > this.maxValue){
50098             this.markInvalid(String.format(this.maxText, this.maxValue));
50099             return false;
50100         }
50101         
50102         return true;
50103     },
50104     
50105     validate : function()
50106     {
50107         if(this.disabled || this.allowBlank){
50108             this.markValid();
50109             return true;
50110         }
50111         
50112         var currency = this.getCurrency();
50113         
50114         if(this.validateValue(this.getRawValue()) && currency.length){
50115             this.markValid();
50116             return true;
50117         }
50118         
50119         this.markInvalid();
50120         return false;
50121     },
50122     
50123     getName: function()
50124     {
50125         return this.name;
50126     },
50127     
50128     beforeBlur : function()
50129     {
50130         if(!this.castInt){
50131             return;
50132         }
50133         
50134         var v = this.parseValue(this.getRawValue());
50135         
50136         if(v || v == 0){
50137             this.setValue(v);
50138         }
50139     },
50140     
50141     onBlur : function()
50142     {
50143         this.beforeBlur();
50144         
50145         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
50146             //this.el.removeClass(this.focusClass);
50147         }
50148         
50149         this.hasFocus = false;
50150         
50151         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
50152             this.validate();
50153         }
50154         
50155         var v = this.getValue();
50156         
50157         if(String(v) !== String(this.startValue)){
50158             this.fireEvent('change', this, v, this.startValue);
50159         }
50160         
50161         this.fireEvent("blur", this);
50162     },
50163     
50164     inputEl : function()
50165     {
50166         return this.el.select('.roo-money-amount-input', true).first();
50167     },
50168     
50169     currencyEl : function()
50170     {
50171         return this.el.select('.roo-money-currency-input', true).first();
50172     },
50173     
50174     hiddenEl : function()
50175     {
50176         return this.el.select('input.hidden-number-input',true).first();
50177     }
50178     
50179 });/**
50180  * @class Roo.bootstrap.BezierSignature
50181  * @extends Roo.bootstrap.Component
50182  * Bootstrap BezierSignature class
50183  * This script refer to:
50184  *    Title: Signature Pad
50185  *    Author: szimek
50186  *    Availability: https://github.com/szimek/signature_pad
50187  *
50188  * @constructor
50189  * Create a new BezierSignature
50190  * @param {Object} config The config object
50191  */
50192
50193 Roo.bootstrap.BezierSignature = function(config){
50194     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
50195     this.addEvents({
50196         "resize" : true
50197     });
50198 };
50199
50200 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
50201 {
50202      
50203     curve_data: [],
50204     
50205     is_empty: true,
50206     
50207     mouse_btn_down: true,
50208     
50209     /**
50210      * @cfg {int} canvas height
50211      */
50212     canvas_height: '200px',
50213     
50214     /**
50215      * @cfg {float|function} Radius of a single dot.
50216      */ 
50217     dot_size: false,
50218     
50219     /**
50220      * @cfg {float} Minimum width of a line. Defaults to 0.5.
50221      */
50222     min_width: 0.5,
50223     
50224     /**
50225      * @cfg {float} Maximum width of a line. Defaults to 2.5.
50226      */
50227     max_width: 2.5,
50228     
50229     /**
50230      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
50231      */
50232     throttle: 16,
50233     
50234     /**
50235      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
50236      */
50237     min_distance: 5,
50238     
50239     /**
50240      * @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.
50241      */
50242     bg_color: 'rgba(0, 0, 0, 0)',
50243     
50244     /**
50245      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
50246      */
50247     dot_color: 'black',
50248     
50249     /**
50250      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
50251      */ 
50252     velocity_filter_weight: 0.7,
50253     
50254     /**
50255      * @cfg {function} Callback when stroke begin. 
50256      */
50257     onBegin: false,
50258     
50259     /**
50260      * @cfg {function} Callback when stroke end.
50261      */
50262     onEnd: false,
50263     
50264     getAutoCreate : function()
50265     {
50266         var cls = 'roo-signature column';
50267         
50268         if(this.cls){
50269             cls += ' ' + this.cls;
50270         }
50271         
50272         var col_sizes = [
50273             'lg',
50274             'md',
50275             'sm',
50276             'xs'
50277         ];
50278         
50279         for(var i = 0; i < col_sizes.length; i++) {
50280             if(this[col_sizes[i]]) {
50281                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
50282             }
50283         }
50284         
50285         var cfg = {
50286             tag: 'div',
50287             cls: cls,
50288             cn: [
50289                 {
50290                     tag: 'div',
50291                     cls: 'roo-signature-body',
50292                     cn: [
50293                         {
50294                             tag: 'canvas',
50295                             cls: 'roo-signature-body-canvas',
50296                             height: this.canvas_height,
50297                             width: this.canvas_width
50298                         }
50299                     ]
50300                 },
50301                 {
50302                     tag: 'input',
50303                     type: 'file',
50304                     style: 'display: none'
50305                 }
50306             ]
50307         };
50308         
50309         return cfg;
50310     },
50311     
50312     initEvents: function() 
50313     {
50314         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
50315         
50316         var canvas = this.canvasEl();
50317         
50318         // mouse && touch event swapping...
50319         canvas.dom.style.touchAction = 'none';
50320         canvas.dom.style.msTouchAction = 'none';
50321         
50322         this.mouse_btn_down = false;
50323         canvas.on('mousedown', this._handleMouseDown, this);
50324         canvas.on('mousemove', this._handleMouseMove, this);
50325         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
50326         
50327         if (window.PointerEvent) {
50328             canvas.on('pointerdown', this._handleMouseDown, this);
50329             canvas.on('pointermove', this._handleMouseMove, this);
50330             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
50331         }
50332         
50333         if ('ontouchstart' in window) {
50334             canvas.on('touchstart', this._handleTouchStart, this);
50335             canvas.on('touchmove', this._handleTouchMove, this);
50336             canvas.on('touchend', this._handleTouchEnd, this);
50337         }
50338         
50339         Roo.EventManager.onWindowResize(this.resize, this, true);
50340         
50341         // file input event
50342         this.fileEl().on('change', this.uploadImage, this);
50343         
50344         this.clear();
50345         
50346         this.resize();
50347     },
50348     
50349     resize: function(){
50350         
50351         var canvas = this.canvasEl().dom;
50352         var ctx = this.canvasElCtx();
50353         var img_data = false;
50354         
50355         if(canvas.width > 0) {
50356             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
50357         }
50358         // setting canvas width will clean img data
50359         canvas.width = 0;
50360         
50361         var style = window.getComputedStyle ? 
50362             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
50363             
50364         var padding_left = parseInt(style.paddingLeft) || 0;
50365         var padding_right = parseInt(style.paddingRight) || 0;
50366         
50367         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
50368         
50369         if(img_data) {
50370             ctx.putImageData(img_data, 0, 0);
50371         }
50372     },
50373     
50374     _handleMouseDown: function(e)
50375     {
50376         if (e.browserEvent.which === 1) {
50377             this.mouse_btn_down = true;
50378             this.strokeBegin(e);
50379         }
50380     },
50381     
50382     _handleMouseMove: function (e)
50383     {
50384         if (this.mouse_btn_down) {
50385             this.strokeMoveUpdate(e);
50386         }
50387     },
50388     
50389     _handleMouseUp: function (e)
50390     {
50391         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
50392             this.mouse_btn_down = false;
50393             this.strokeEnd(e);
50394         }
50395     },
50396     
50397     _handleTouchStart: function (e) {
50398         
50399         e.preventDefault();
50400         if (e.browserEvent.targetTouches.length === 1) {
50401             // var touch = e.browserEvent.changedTouches[0];
50402             // this.strokeBegin(touch);
50403             
50404              this.strokeBegin(e); // assume e catching the correct xy...
50405         }
50406     },
50407     
50408     _handleTouchMove: function (e) {
50409         e.preventDefault();
50410         // var touch = event.targetTouches[0];
50411         // _this._strokeMoveUpdate(touch);
50412         this.strokeMoveUpdate(e);
50413     },
50414     
50415     _handleTouchEnd: function (e) {
50416         var wasCanvasTouched = e.target === this.canvasEl().dom;
50417         if (wasCanvasTouched) {
50418             e.preventDefault();
50419             // var touch = event.changedTouches[0];
50420             // _this._strokeEnd(touch);
50421             this.strokeEnd(e);
50422         }
50423     },
50424     
50425     reset: function () {
50426         this._lastPoints = [];
50427         this._lastVelocity = 0;
50428         this._lastWidth = (this.min_width + this.max_width) / 2;
50429         this.canvasElCtx().fillStyle = this.dot_color;
50430     },
50431     
50432     strokeMoveUpdate: function(e)
50433     {
50434         this.strokeUpdate(e);
50435         
50436         if (this.throttle) {
50437             this.throttleStroke(this.strokeUpdate, this.throttle);
50438         }
50439         else {
50440             this.strokeUpdate(e);
50441         }
50442     },
50443     
50444     strokeBegin: function(e)
50445     {
50446         var newPointGroup = {
50447             color: this.dot_color,
50448             points: []
50449         };
50450         
50451         if (typeof this.onBegin === 'function') {
50452             this.onBegin(e);
50453         }
50454         
50455         this.curve_data.push(newPointGroup);
50456         this.reset();
50457         this.strokeUpdate(e);
50458     },
50459     
50460     strokeUpdate: function(e)
50461     {
50462         var rect = this.canvasEl().dom.getBoundingClientRect();
50463         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
50464         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
50465         var lastPoints = lastPointGroup.points;
50466         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
50467         var isLastPointTooClose = lastPoint
50468             ? point.distanceTo(lastPoint) <= this.min_distance
50469             : false;
50470         var color = lastPointGroup.color;
50471         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
50472             var curve = this.addPoint(point);
50473             if (!lastPoint) {
50474                 this.drawDot({color: color, point: point});
50475             }
50476             else if (curve) {
50477                 this.drawCurve({color: color, curve: curve});
50478             }
50479             lastPoints.push({
50480                 time: point.time,
50481                 x: point.x,
50482                 y: point.y
50483             });
50484         }
50485     },
50486     
50487     strokeEnd: function(e)
50488     {
50489         this.strokeUpdate(e);
50490         if (typeof this.onEnd === 'function') {
50491             this.onEnd(e);
50492         }
50493     },
50494     
50495     addPoint:  function (point) {
50496         var _lastPoints = this._lastPoints;
50497         _lastPoints.push(point);
50498         if (_lastPoints.length > 2) {
50499             if (_lastPoints.length === 3) {
50500                 _lastPoints.unshift(_lastPoints[0]);
50501             }
50502             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
50503             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
50504             _lastPoints.shift();
50505             return curve;
50506         }
50507         return null;
50508     },
50509     
50510     calculateCurveWidths: function (startPoint, endPoint) {
50511         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
50512             (1 - this.velocity_filter_weight) * this._lastVelocity;
50513
50514         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
50515         var widths = {
50516             end: newWidth,
50517             start: this._lastWidth
50518         };
50519         
50520         this._lastVelocity = velocity;
50521         this._lastWidth = newWidth;
50522         return widths;
50523     },
50524     
50525     drawDot: function (_a) {
50526         var color = _a.color, point = _a.point;
50527         var ctx = this.canvasElCtx();
50528         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
50529         ctx.beginPath();
50530         this.drawCurveSegment(point.x, point.y, width);
50531         ctx.closePath();
50532         ctx.fillStyle = color;
50533         ctx.fill();
50534     },
50535     
50536     drawCurve: function (_a) {
50537         var color = _a.color, curve = _a.curve;
50538         var ctx = this.canvasElCtx();
50539         var widthDelta = curve.endWidth - curve.startWidth;
50540         var drawSteps = Math.floor(curve.length()) * 2;
50541         ctx.beginPath();
50542         ctx.fillStyle = color;
50543         for (var i = 0; i < drawSteps; i += 1) {
50544         var t = i / drawSteps;
50545         var tt = t * t;
50546         var ttt = tt * t;
50547         var u = 1 - t;
50548         var uu = u * u;
50549         var uuu = uu * u;
50550         var x = uuu * curve.startPoint.x;
50551         x += 3 * uu * t * curve.control1.x;
50552         x += 3 * u * tt * curve.control2.x;
50553         x += ttt * curve.endPoint.x;
50554         var y = uuu * curve.startPoint.y;
50555         y += 3 * uu * t * curve.control1.y;
50556         y += 3 * u * tt * curve.control2.y;
50557         y += ttt * curve.endPoint.y;
50558         var width = curve.startWidth + ttt * widthDelta;
50559         this.drawCurveSegment(x, y, width);
50560         }
50561         ctx.closePath();
50562         ctx.fill();
50563     },
50564     
50565     drawCurveSegment: function (x, y, width) {
50566         var ctx = this.canvasElCtx();
50567         ctx.moveTo(x, y);
50568         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
50569         this.is_empty = false;
50570     },
50571     
50572     clear: function()
50573     {
50574         var ctx = this.canvasElCtx();
50575         var canvas = this.canvasEl().dom;
50576         ctx.fillStyle = this.bg_color;
50577         ctx.clearRect(0, 0, canvas.width, canvas.height);
50578         ctx.fillRect(0, 0, canvas.width, canvas.height);
50579         this.curve_data = [];
50580         this.reset();
50581         this.is_empty = true;
50582     },
50583     
50584     fileEl: function()
50585     {
50586         return  this.el.select('input',true).first();
50587     },
50588     
50589     canvasEl: function()
50590     {
50591         return this.el.select('canvas',true).first();
50592     },
50593     
50594     canvasElCtx: function()
50595     {
50596         return this.el.select('canvas',true).first().dom.getContext('2d');
50597     },
50598     
50599     getImage: function(type)
50600     {
50601         if(this.is_empty) {
50602             return false;
50603         }
50604         
50605         // encryption ?
50606         return this.canvasEl().dom.toDataURL('image/'+type, 1);
50607     },
50608     
50609     drawFromImage: function(img_src)
50610     {
50611         var img = new Image();
50612         
50613         img.onload = function(){
50614             this.canvasElCtx().drawImage(img, 0, 0);
50615         }.bind(this);
50616         
50617         img.src = img_src;
50618         
50619         this.is_empty = false;
50620     },
50621     
50622     selectImage: function()
50623     {
50624         this.fileEl().dom.click();
50625     },
50626     
50627     uploadImage: function(e)
50628     {
50629         var reader = new FileReader();
50630         
50631         reader.onload = function(e){
50632             var img = new Image();
50633             img.onload = function(){
50634                 this.reset();
50635                 this.canvasElCtx().drawImage(img, 0, 0);
50636             }.bind(this);
50637             img.src = e.target.result;
50638         }.bind(this);
50639         
50640         reader.readAsDataURL(e.target.files[0]);
50641     },
50642     
50643     // Bezier Point Constructor
50644     Point: (function () {
50645         function Point(x, y, time) {
50646             this.x = x;
50647             this.y = y;
50648             this.time = time || Date.now();
50649         }
50650         Point.prototype.distanceTo = function (start) {
50651             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50652         };
50653         Point.prototype.equals = function (other) {
50654             return this.x === other.x && this.y === other.y && this.time === other.time;
50655         };
50656         Point.prototype.velocityFrom = function (start) {
50657             return this.time !== start.time
50658             ? this.distanceTo(start) / (this.time - start.time)
50659             : 0;
50660         };
50661         return Point;
50662     }()),
50663     
50664     
50665     // Bezier Constructor
50666     Bezier: (function () {
50667         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50668             this.startPoint = startPoint;
50669             this.control2 = control2;
50670             this.control1 = control1;
50671             this.endPoint = endPoint;
50672             this.startWidth = startWidth;
50673             this.endWidth = endWidth;
50674         }
50675         Bezier.fromPoints = function (points, widths, scope) {
50676             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50677             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50678             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50679         };
50680         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50681             var dx1 = s1.x - s2.x;
50682             var dy1 = s1.y - s2.y;
50683             var dx2 = s2.x - s3.x;
50684             var dy2 = s2.y - s3.y;
50685             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50686             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50687             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50688             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50689             var dxm = m1.x - m2.x;
50690             var dym = m1.y - m2.y;
50691             var k = l2 / (l1 + l2);
50692             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50693             var tx = s2.x - cm.x;
50694             var ty = s2.y - cm.y;
50695             return {
50696                 c1: new scope.Point(m1.x + tx, m1.y + ty),
50697                 c2: new scope.Point(m2.x + tx, m2.y + ty)
50698             };
50699         };
50700         Bezier.prototype.length = function () {
50701             var steps = 10;
50702             var length = 0;
50703             var px;
50704             var py;
50705             for (var i = 0; i <= steps; i += 1) {
50706                 var t = i / steps;
50707                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50708                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50709                 if (i > 0) {
50710                     var xdiff = cx - px;
50711                     var ydiff = cy - py;
50712                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50713                 }
50714                 px = cx;
50715                 py = cy;
50716             }
50717             return length;
50718         };
50719         Bezier.prototype.point = function (t, start, c1, c2, end) {
50720             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50721             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50722             + (3.0 * c2 * (1.0 - t) * t * t)
50723             + (end * t * t * t);
50724         };
50725         return Bezier;
50726     }()),
50727     
50728     throttleStroke: function(fn, wait) {
50729       if (wait === void 0) { wait = 250; }
50730       var previous = 0;
50731       var timeout = null;
50732       var result;
50733       var storedContext;
50734       var storedArgs;
50735       var later = function () {
50736           previous = Date.now();
50737           timeout = null;
50738           result = fn.apply(storedContext, storedArgs);
50739           if (!timeout) {
50740               storedContext = null;
50741               storedArgs = [];
50742           }
50743       };
50744       return function wrapper() {
50745           var args = [];
50746           for (var _i = 0; _i < arguments.length; _i++) {
50747               args[_i] = arguments[_i];
50748           }
50749           var now = Date.now();
50750           var remaining = wait - (now - previous);
50751           storedContext = this;
50752           storedArgs = args;
50753           if (remaining <= 0 || remaining > wait) {
50754               if (timeout) {
50755                   clearTimeout(timeout);
50756                   timeout = null;
50757               }
50758               previous = now;
50759               result = fn.apply(storedContext, storedArgs);
50760               if (!timeout) {
50761                   storedContext = null;
50762                   storedArgs = [];
50763               }
50764           }
50765           else if (!timeout) {
50766               timeout = window.setTimeout(later, remaining);
50767           }
50768           return result;
50769       };
50770   }
50771   
50772 });
50773
50774  
50775
50776  // old names for form elements
50777 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
50778 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
50779 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
50780 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
50781 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
50782 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
50783 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
50784 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
50785 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
50786 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
50787 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
50788 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
50789 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
50790 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
50791 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
50792 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
50793 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
50794 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
50795 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
50796 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
50797 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
50798 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
50799 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
50800 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
50801 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
50802 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
50803
50804 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
50805 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50806
50807 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
50808 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
50809
50810 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
50811 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50812 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
50813 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
50814