set dynamic max height for bootstrap combo box
[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.el.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  * Based on:
14757  * Ext JS Library 1.1.1
14758  * Copyright(c) 2006-2007, Ext JS, LLC.
14759  *
14760  * Originally Released Under LGPL - original licence link has changed is not relivant.
14761  *
14762  * Fork - LGPL
14763  * <script type="text/javascript">
14764  */
14765
14766
14767 /**
14768  * @class Roo.data.SortTypes
14769  * @static
14770  * Defines the default sorting (casting?) comparison functions used when sorting data.
14771  */
14772 Roo.data.SortTypes = {
14773     /**
14774      * Default sort that does nothing
14775      * @param {Mixed} s The value being converted
14776      * @return {Mixed} The comparison value
14777      */
14778     none : function(s){
14779         return s;
14780     },
14781     
14782     /**
14783      * The regular expression used to strip tags
14784      * @type {RegExp}
14785      * @property
14786      */
14787     stripTagsRE : /<\/?[^>]+>/gi,
14788     
14789     /**
14790      * Strips all HTML tags to sort on text only
14791      * @param {Mixed} s The value being converted
14792      * @return {String} The comparison value
14793      */
14794     asText : function(s){
14795         return String(s).replace(this.stripTagsRE, "");
14796     },
14797     
14798     /**
14799      * Strips all HTML tags to sort on text only - Case insensitive
14800      * @param {Mixed} s The value being converted
14801      * @return {String} The comparison value
14802      */
14803     asUCText : function(s){
14804         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14805     },
14806     
14807     /**
14808      * Case insensitive string
14809      * @param {Mixed} s The value being converted
14810      * @return {String} The comparison value
14811      */
14812     asUCString : function(s) {
14813         return String(s).toUpperCase();
14814     },
14815     
14816     /**
14817      * Date sorting
14818      * @param {Mixed} s The value being converted
14819      * @return {Number} The comparison value
14820      */
14821     asDate : function(s) {
14822         if(!s){
14823             return 0;
14824         }
14825         if(s instanceof Date){
14826             return s.getTime();
14827         }
14828         return Date.parse(String(s));
14829     },
14830     
14831     /**
14832      * Float sorting
14833      * @param {Mixed} s The value being converted
14834      * @return {Float} The comparison value
14835      */
14836     asFloat : function(s) {
14837         var val = parseFloat(String(s).replace(/,/g, ""));
14838         if(isNaN(val)) {
14839             val = 0;
14840         }
14841         return val;
14842     },
14843     
14844     /**
14845      * Integer sorting
14846      * @param {Mixed} s The value being converted
14847      * @return {Number} The comparison value
14848      */
14849     asInt : function(s) {
14850         var val = parseInt(String(s).replace(/,/g, ""));
14851         if(isNaN(val)) {
14852             val = 0;
14853         }
14854         return val;
14855     }
14856 };/*
14857  * Based on:
14858  * Ext JS Library 1.1.1
14859  * Copyright(c) 2006-2007, Ext JS, LLC.
14860  *
14861  * Originally Released Under LGPL - original licence link has changed is not relivant.
14862  *
14863  * Fork - LGPL
14864  * <script type="text/javascript">
14865  */
14866
14867 /**
14868 * @class Roo.data.Record
14869  * Instances of this class encapsulate both record <em>definition</em> information, and record
14870  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14871  * to access Records cached in an {@link Roo.data.Store} object.<br>
14872  * <p>
14873  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14874  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14875  * objects.<br>
14876  * <p>
14877  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14878  * @constructor
14879  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14880  * {@link #create}. The parameters are the same.
14881  * @param {Array} data An associative Array of data values keyed by the field name.
14882  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14883  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14884  * not specified an integer id is generated.
14885  */
14886 Roo.data.Record = function(data, id){
14887     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14888     this.data = data;
14889 };
14890
14891 /**
14892  * Generate a constructor for a specific record layout.
14893  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14894  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14895  * Each field definition object may contain the following properties: <ul>
14896  * <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,
14897  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14898  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14899  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14900  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14901  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14902  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14903  * this may be omitted.</p></li>
14904  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14905  * <ul><li>auto (Default, implies no conversion)</li>
14906  * <li>string</li>
14907  * <li>int</li>
14908  * <li>float</li>
14909  * <li>boolean</li>
14910  * <li>date</li></ul></p></li>
14911  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14912  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14913  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14914  * by the Reader into an object that will be stored in the Record. It is passed the
14915  * following parameters:<ul>
14916  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14917  * </ul></p></li>
14918  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14919  * </ul>
14920  * <br>usage:<br><pre><code>
14921 var TopicRecord = Roo.data.Record.create(
14922     {name: 'title', mapping: 'topic_title'},
14923     {name: 'author', mapping: 'username'},
14924     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14925     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14926     {name: 'lastPoster', mapping: 'user2'},
14927     {name: 'excerpt', mapping: 'post_text'}
14928 );
14929
14930 var myNewRecord = new TopicRecord({
14931     title: 'Do my job please',
14932     author: 'noobie',
14933     totalPosts: 1,
14934     lastPost: new Date(),
14935     lastPoster: 'Animal',
14936     excerpt: 'No way dude!'
14937 });
14938 myStore.add(myNewRecord);
14939 </code></pre>
14940  * @method create
14941  * @static
14942  */
14943 Roo.data.Record.create = function(o){
14944     var f = function(){
14945         f.superclass.constructor.apply(this, arguments);
14946     };
14947     Roo.extend(f, Roo.data.Record);
14948     var p = f.prototype;
14949     p.fields = new Roo.util.MixedCollection(false, function(field){
14950         return field.name;
14951     });
14952     for(var i = 0, len = o.length; i < len; i++){
14953         p.fields.add(new Roo.data.Field(o[i]));
14954     }
14955     f.getField = function(name){
14956         return p.fields.get(name);  
14957     };
14958     return f;
14959 };
14960
14961 Roo.data.Record.AUTO_ID = 1000;
14962 Roo.data.Record.EDIT = 'edit';
14963 Roo.data.Record.REJECT = 'reject';
14964 Roo.data.Record.COMMIT = 'commit';
14965
14966 Roo.data.Record.prototype = {
14967     /**
14968      * Readonly flag - true if this record has been modified.
14969      * @type Boolean
14970      */
14971     dirty : false,
14972     editing : false,
14973     error: null,
14974     modified: null,
14975
14976     // private
14977     join : function(store){
14978         this.store = store;
14979     },
14980
14981     /**
14982      * Set the named field to the specified value.
14983      * @param {String} name The name of the field to set.
14984      * @param {Object} value The value to set the field to.
14985      */
14986     set : function(name, value){
14987         if(this.data[name] == value){
14988             return;
14989         }
14990         this.dirty = true;
14991         if(!this.modified){
14992             this.modified = {};
14993         }
14994         if(typeof this.modified[name] == 'undefined'){
14995             this.modified[name] = this.data[name];
14996         }
14997         this.data[name] = value;
14998         if(!this.editing && this.store){
14999             this.store.afterEdit(this);
15000         }       
15001     },
15002
15003     /**
15004      * Get the value of the named field.
15005      * @param {String} name The name of the field to get the value of.
15006      * @return {Object} The value of the field.
15007      */
15008     get : function(name){
15009         return this.data[name]; 
15010     },
15011
15012     // private
15013     beginEdit : function(){
15014         this.editing = true;
15015         this.modified = {}; 
15016     },
15017
15018     // private
15019     cancelEdit : function(){
15020         this.editing = false;
15021         delete this.modified;
15022     },
15023
15024     // private
15025     endEdit : function(){
15026         this.editing = false;
15027         if(this.dirty && this.store){
15028             this.store.afterEdit(this);
15029         }
15030     },
15031
15032     /**
15033      * Usually called by the {@link Roo.data.Store} which owns the Record.
15034      * Rejects all changes made to the Record since either creation, or the last commit operation.
15035      * Modified fields are reverted to their original values.
15036      * <p>
15037      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15038      * of reject operations.
15039      */
15040     reject : function(){
15041         var m = this.modified;
15042         for(var n in m){
15043             if(typeof m[n] != "function"){
15044                 this.data[n] = m[n];
15045             }
15046         }
15047         this.dirty = false;
15048         delete this.modified;
15049         this.editing = false;
15050         if(this.store){
15051             this.store.afterReject(this);
15052         }
15053     },
15054
15055     /**
15056      * Usually called by the {@link Roo.data.Store} which owns the Record.
15057      * Commits all changes made to the Record since either creation, or the last commit operation.
15058      * <p>
15059      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15060      * of commit operations.
15061      */
15062     commit : function(){
15063         this.dirty = false;
15064         delete this.modified;
15065         this.editing = false;
15066         if(this.store){
15067             this.store.afterCommit(this);
15068         }
15069     },
15070
15071     // private
15072     hasError : function(){
15073         return this.error != null;
15074     },
15075
15076     // private
15077     clearError : function(){
15078         this.error = null;
15079     },
15080
15081     /**
15082      * Creates a copy of this record.
15083      * @param {String} id (optional) A new record id if you don't want to use this record's id
15084      * @return {Record}
15085      */
15086     copy : function(newId) {
15087         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15088     }
15089 };/*
15090  * Based on:
15091  * Ext JS Library 1.1.1
15092  * Copyright(c) 2006-2007, Ext JS, LLC.
15093  *
15094  * Originally Released Under LGPL - original licence link has changed is not relivant.
15095  *
15096  * Fork - LGPL
15097  * <script type="text/javascript">
15098  */
15099
15100
15101
15102 /**
15103  * @class Roo.data.Store
15104  * @extends Roo.util.Observable
15105  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15106  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15107  * <p>
15108  * 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
15109  * has no knowledge of the format of the data returned by the Proxy.<br>
15110  * <p>
15111  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15112  * instances from the data object. These records are cached and made available through accessor functions.
15113  * @constructor
15114  * Creates a new Store.
15115  * @param {Object} config A config object containing the objects needed for the Store to access data,
15116  * and read the data into Records.
15117  */
15118 Roo.data.Store = function(config){
15119     this.data = new Roo.util.MixedCollection(false);
15120     this.data.getKey = function(o){
15121         return o.id;
15122     };
15123     this.baseParams = {};
15124     // private
15125     this.paramNames = {
15126         "start" : "start",
15127         "limit" : "limit",
15128         "sort" : "sort",
15129         "dir" : "dir",
15130         "multisort" : "_multisort"
15131     };
15132
15133     if(config && config.data){
15134         this.inlineData = config.data;
15135         delete config.data;
15136     }
15137
15138     Roo.apply(this, config);
15139     
15140     if(this.reader){ // reader passed
15141         this.reader = Roo.factory(this.reader, Roo.data);
15142         this.reader.xmodule = this.xmodule || false;
15143         if(!this.recordType){
15144             this.recordType = this.reader.recordType;
15145         }
15146         if(this.reader.onMetaChange){
15147             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15148         }
15149     }
15150
15151     if(this.recordType){
15152         this.fields = this.recordType.prototype.fields;
15153     }
15154     this.modified = [];
15155
15156     this.addEvents({
15157         /**
15158          * @event datachanged
15159          * Fires when the data cache has changed, and a widget which is using this Store
15160          * as a Record cache should refresh its view.
15161          * @param {Store} this
15162          */
15163         datachanged : true,
15164         /**
15165          * @event metachange
15166          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15167          * @param {Store} this
15168          * @param {Object} meta The JSON metadata
15169          */
15170         metachange : true,
15171         /**
15172          * @event add
15173          * Fires when Records have been added to the Store
15174          * @param {Store} this
15175          * @param {Roo.data.Record[]} records The array of Records added
15176          * @param {Number} index The index at which the record(s) were added
15177          */
15178         add : true,
15179         /**
15180          * @event remove
15181          * Fires when a Record has been removed from the Store
15182          * @param {Store} this
15183          * @param {Roo.data.Record} record The Record that was removed
15184          * @param {Number} index The index at which the record was removed
15185          */
15186         remove : true,
15187         /**
15188          * @event update
15189          * Fires when a Record has been updated
15190          * @param {Store} this
15191          * @param {Roo.data.Record} record The Record that was updated
15192          * @param {String} operation The update operation being performed.  Value may be one of:
15193          * <pre><code>
15194  Roo.data.Record.EDIT
15195  Roo.data.Record.REJECT
15196  Roo.data.Record.COMMIT
15197          * </code></pre>
15198          */
15199         update : true,
15200         /**
15201          * @event clear
15202          * Fires when the data cache has been cleared.
15203          * @param {Store} this
15204          */
15205         clear : true,
15206         /**
15207          * @event beforeload
15208          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15209          * the load action will be canceled.
15210          * @param {Store} this
15211          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15212          */
15213         beforeload : true,
15214         /**
15215          * @event beforeloadadd
15216          * Fires after a new set of Records has been loaded.
15217          * @param {Store} this
15218          * @param {Roo.data.Record[]} records The Records that were loaded
15219          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15220          */
15221         beforeloadadd : true,
15222         /**
15223          * @event load
15224          * Fires after a new set of Records has been loaded, before they are added to the store.
15225          * @param {Store} this
15226          * @param {Roo.data.Record[]} records The Records that were loaded
15227          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15228          * @params {Object} return from reader
15229          */
15230         load : true,
15231         /**
15232          * @event loadexception
15233          * Fires if an exception occurs in the Proxy during loading.
15234          * Called with the signature of the Proxy's "loadexception" event.
15235          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15236          * 
15237          * @param {Proxy} 
15238          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15239          * @param {Object} opts - load Options
15240          * @param {Object} jsonData from your request (normally this contains the Exception)
15241          */
15242         loadexception : true
15243     });
15244     
15245     if(this.proxy){
15246         this.proxy = Roo.factory(this.proxy, Roo.data);
15247         this.proxy.xmodule = this.xmodule || false;
15248         this.relayEvents(this.proxy,  ["loadexception"]);
15249     }
15250     this.sortToggle = {};
15251     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15252
15253     Roo.data.Store.superclass.constructor.call(this);
15254
15255     if(this.inlineData){
15256         this.loadData(this.inlineData);
15257         delete this.inlineData;
15258     }
15259 };
15260
15261 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15262      /**
15263     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15264     * without a remote query - used by combo/forms at present.
15265     */
15266     
15267     /**
15268     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15269     */
15270     /**
15271     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15272     */
15273     /**
15274     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15275     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15276     */
15277     /**
15278     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15279     * on any HTTP request
15280     */
15281     /**
15282     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15283     */
15284     /**
15285     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15286     */
15287     multiSort: false,
15288     /**
15289     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15290     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15291     */
15292     remoteSort : false,
15293
15294     /**
15295     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15296      * loaded or when a record is removed. (defaults to false).
15297     */
15298     pruneModifiedRecords : false,
15299
15300     // private
15301     lastOptions : null,
15302
15303     /**
15304      * Add Records to the Store and fires the add event.
15305      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15306      */
15307     add : function(records){
15308         records = [].concat(records);
15309         for(var i = 0, len = records.length; i < len; i++){
15310             records[i].join(this);
15311         }
15312         var index = this.data.length;
15313         this.data.addAll(records);
15314         this.fireEvent("add", this, records, index);
15315     },
15316
15317     /**
15318      * Remove a Record from the Store and fires the remove event.
15319      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15320      */
15321     remove : function(record){
15322         var index = this.data.indexOf(record);
15323         this.data.removeAt(index);
15324  
15325         if(this.pruneModifiedRecords){
15326             this.modified.remove(record);
15327         }
15328         this.fireEvent("remove", this, record, index);
15329     },
15330
15331     /**
15332      * Remove all Records from the Store and fires the clear event.
15333      */
15334     removeAll : function(){
15335         this.data.clear();
15336         if(this.pruneModifiedRecords){
15337             this.modified = [];
15338         }
15339         this.fireEvent("clear", this);
15340     },
15341
15342     /**
15343      * Inserts Records to the Store at the given index and fires the add event.
15344      * @param {Number} index The start index at which to insert the passed Records.
15345      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15346      */
15347     insert : function(index, records){
15348         records = [].concat(records);
15349         for(var i = 0, len = records.length; i < len; i++){
15350             this.data.insert(index, records[i]);
15351             records[i].join(this);
15352         }
15353         this.fireEvent("add", this, records, index);
15354     },
15355
15356     /**
15357      * Get the index within the cache of the passed Record.
15358      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15359      * @return {Number} The index of the passed Record. Returns -1 if not found.
15360      */
15361     indexOf : function(record){
15362         return this.data.indexOf(record);
15363     },
15364
15365     /**
15366      * Get the index within the cache of the Record with the passed id.
15367      * @param {String} id The id of the Record to find.
15368      * @return {Number} The index of the Record. Returns -1 if not found.
15369      */
15370     indexOfId : function(id){
15371         return this.data.indexOfKey(id);
15372     },
15373
15374     /**
15375      * Get the Record with the specified id.
15376      * @param {String} id The id of the Record to find.
15377      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15378      */
15379     getById : function(id){
15380         return this.data.key(id);
15381     },
15382
15383     /**
15384      * Get the Record at the specified index.
15385      * @param {Number} index The index of the Record to find.
15386      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15387      */
15388     getAt : function(index){
15389         return this.data.itemAt(index);
15390     },
15391
15392     /**
15393      * Returns a range of Records between specified indices.
15394      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15395      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15396      * @return {Roo.data.Record[]} An array of Records
15397      */
15398     getRange : function(start, end){
15399         return this.data.getRange(start, end);
15400     },
15401
15402     // private
15403     storeOptions : function(o){
15404         o = Roo.apply({}, o);
15405         delete o.callback;
15406         delete o.scope;
15407         this.lastOptions = o;
15408     },
15409
15410     /**
15411      * Loads the Record cache from the configured Proxy using the configured Reader.
15412      * <p>
15413      * If using remote paging, then the first load call must specify the <em>start</em>
15414      * and <em>limit</em> properties in the options.params property to establish the initial
15415      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15416      * <p>
15417      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15418      * and this call will return before the new data has been loaded. Perform any post-processing
15419      * in a callback function, or in a "load" event handler.</strong>
15420      * <p>
15421      * @param {Object} options An object containing properties which control loading options:<ul>
15422      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15423      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15424      * <pre>
15425                 {
15426                     data : data,  // array of key=>value data like JsonReader
15427                     total : data.length,
15428                     success : true
15429                     
15430                 }
15431         </pre>
15432             }.</li>
15433      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15434      * passed the following arguments:<ul>
15435      * <li>r : Roo.data.Record[]</li>
15436      * <li>options: Options object from the load call</li>
15437      * <li>success: Boolean success indicator</li></ul></li>
15438      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15439      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15440      * </ul>
15441      */
15442     load : function(options){
15443         options = options || {};
15444         if(this.fireEvent("beforeload", this, options) !== false){
15445             this.storeOptions(options);
15446             var p = Roo.apply(options.params || {}, this.baseParams);
15447             // if meta was not loaded from remote source.. try requesting it.
15448             if (!this.reader.metaFromRemote) {
15449                 p._requestMeta = 1;
15450             }
15451             if(this.sortInfo && this.remoteSort){
15452                 var pn = this.paramNames;
15453                 p[pn["sort"]] = this.sortInfo.field;
15454                 p[pn["dir"]] = this.sortInfo.direction;
15455             }
15456             if (this.multiSort) {
15457                 var pn = this.paramNames;
15458                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15459             }
15460             
15461             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15462         }
15463     },
15464
15465     /**
15466      * Reloads the Record cache from the configured Proxy using the configured Reader and
15467      * the options from the last load operation performed.
15468      * @param {Object} options (optional) An object containing properties which may override the options
15469      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15470      * the most recently used options are reused).
15471      */
15472     reload : function(options){
15473         this.load(Roo.applyIf(options||{}, this.lastOptions));
15474     },
15475
15476     // private
15477     // Called as a callback by the Reader during a load operation.
15478     loadRecords : function(o, options, success){
15479          
15480         if(!o){
15481             if(success !== false){
15482                 this.fireEvent("load", this, [], options, o);
15483             }
15484             if(options.callback){
15485                 options.callback.call(options.scope || this, [], options, false);
15486             }
15487             return;
15488         }
15489         // if data returned failure - throw an exception.
15490         if (o.success === false) {
15491             // show a message if no listener is registered.
15492             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15493                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15494             }
15495             // loadmask wil be hooked into this..
15496             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15497             return;
15498         }
15499         var r = o.records, t = o.totalRecords || r.length;
15500         
15501         this.fireEvent("beforeloadadd", this, r, options, o);
15502         
15503         if(!options || options.add !== true){
15504             if(this.pruneModifiedRecords){
15505                 this.modified = [];
15506             }
15507             for(var i = 0, len = r.length; i < len; i++){
15508                 r[i].join(this);
15509             }
15510             if(this.snapshot){
15511                 this.data = this.snapshot;
15512                 delete this.snapshot;
15513             }
15514             this.data.clear();
15515             this.data.addAll(r);
15516             this.totalLength = t;
15517             this.applySort();
15518             this.fireEvent("datachanged", this);
15519         }else{
15520             this.totalLength = Math.max(t, this.data.length+r.length);
15521             this.add(r);
15522         }
15523         
15524         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15525                 
15526             var e = new Roo.data.Record({});
15527
15528             e.set(this.parent.displayField, this.parent.emptyTitle);
15529             e.set(this.parent.valueField, '');
15530
15531             this.insert(0, e);
15532         }
15533             
15534         this.fireEvent("load", this, r, options, o);
15535         if(options.callback){
15536             options.callback.call(options.scope || this, r, options, true);
15537         }
15538     },
15539
15540
15541     /**
15542      * Loads data from a passed data block. A Reader which understands the format of the data
15543      * must have been configured in the constructor.
15544      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15545      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15546      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15547      */
15548     loadData : function(o, append){
15549         var r = this.reader.readRecords(o);
15550         this.loadRecords(r, {add: append}, true);
15551     },
15552     
15553      /**
15554      * using 'cn' the nested child reader read the child array into it's child stores.
15555      * @param {Object} rec The record with a 'children array
15556      */
15557     loadDataFromChildren : function(rec)
15558     {
15559         this.loadData(this.reader.toLoadData(rec));
15560     },
15561     
15562
15563     /**
15564      * Gets the number of cached records.
15565      * <p>
15566      * <em>If using paging, this may not be the total size of the dataset. If the data object
15567      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15568      * the data set size</em>
15569      */
15570     getCount : function(){
15571         return this.data.length || 0;
15572     },
15573
15574     /**
15575      * Gets the total number of records in the dataset as returned by the server.
15576      * <p>
15577      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15578      * the dataset size</em>
15579      */
15580     getTotalCount : function(){
15581         return this.totalLength || 0;
15582     },
15583
15584     /**
15585      * Returns the sort state of the Store as an object with two properties:
15586      * <pre><code>
15587  field {String} The name of the field by which the Records are sorted
15588  direction {String} The sort order, "ASC" or "DESC"
15589      * </code></pre>
15590      */
15591     getSortState : function(){
15592         return this.sortInfo;
15593     },
15594
15595     // private
15596     applySort : function(){
15597         if(this.sortInfo && !this.remoteSort){
15598             var s = this.sortInfo, f = s.field;
15599             var st = this.fields.get(f).sortType;
15600             var fn = function(r1, r2){
15601                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15602                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15603             };
15604             this.data.sort(s.direction, fn);
15605             if(this.snapshot && this.snapshot != this.data){
15606                 this.snapshot.sort(s.direction, fn);
15607             }
15608         }
15609     },
15610
15611     /**
15612      * Sets the default sort column and order to be used by the next load operation.
15613      * @param {String} fieldName The name of the field to sort by.
15614      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15615      */
15616     setDefaultSort : function(field, dir){
15617         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15618     },
15619
15620     /**
15621      * Sort the Records.
15622      * If remote sorting is used, the sort is performed on the server, and the cache is
15623      * reloaded. If local sorting is used, the cache is sorted internally.
15624      * @param {String} fieldName The name of the field to sort by.
15625      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15626      */
15627     sort : function(fieldName, dir){
15628         var f = this.fields.get(fieldName);
15629         if(!dir){
15630             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15631             
15632             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15633                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15634             }else{
15635                 dir = f.sortDir;
15636             }
15637         }
15638         this.sortToggle[f.name] = dir;
15639         this.sortInfo = {field: f.name, direction: dir};
15640         if(!this.remoteSort){
15641             this.applySort();
15642             this.fireEvent("datachanged", this);
15643         }else{
15644             this.load(this.lastOptions);
15645         }
15646     },
15647
15648     /**
15649      * Calls the specified function for each of the Records in the cache.
15650      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15651      * Returning <em>false</em> aborts and exits the iteration.
15652      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15653      */
15654     each : function(fn, scope){
15655         this.data.each(fn, scope);
15656     },
15657
15658     /**
15659      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15660      * (e.g., during paging).
15661      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15662      */
15663     getModifiedRecords : function(){
15664         return this.modified;
15665     },
15666
15667     // private
15668     createFilterFn : function(property, value, anyMatch){
15669         if(!value.exec){ // not a regex
15670             value = String(value);
15671             if(value.length == 0){
15672                 return false;
15673             }
15674             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15675         }
15676         return function(r){
15677             return value.test(r.data[property]);
15678         };
15679     },
15680
15681     /**
15682      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15683      * @param {String} property A field on your records
15684      * @param {Number} start The record index to start at (defaults to 0)
15685      * @param {Number} end The last record index to include (defaults to length - 1)
15686      * @return {Number} The sum
15687      */
15688     sum : function(property, start, end){
15689         var rs = this.data.items, v = 0;
15690         start = start || 0;
15691         end = (end || end === 0) ? end : rs.length-1;
15692
15693         for(var i = start; i <= end; i++){
15694             v += (rs[i].data[property] || 0);
15695         }
15696         return v;
15697     },
15698
15699     /**
15700      * Filter the records by a specified property.
15701      * @param {String} field A field on your records
15702      * @param {String/RegExp} value Either a string that the field
15703      * should start with or a RegExp to test against the field
15704      * @param {Boolean} anyMatch True to match any part not just the beginning
15705      */
15706     filter : function(property, value, anyMatch){
15707         var fn = this.createFilterFn(property, value, anyMatch);
15708         return fn ? this.filterBy(fn) : this.clearFilter();
15709     },
15710
15711     /**
15712      * Filter by a function. The specified function will be called with each
15713      * record in this data source. If the function returns true the record is included,
15714      * otherwise it is filtered.
15715      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15716      * @param {Object} scope (optional) The scope of the function (defaults to this)
15717      */
15718     filterBy : function(fn, scope){
15719         this.snapshot = this.snapshot || this.data;
15720         this.data = this.queryBy(fn, scope||this);
15721         this.fireEvent("datachanged", this);
15722     },
15723
15724     /**
15725      * Query the records by a specified property.
15726      * @param {String} field A field on your records
15727      * @param {String/RegExp} value Either a string that the field
15728      * should start with or a RegExp to test against the field
15729      * @param {Boolean} anyMatch True to match any part not just the beginning
15730      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15731      */
15732     query : function(property, value, anyMatch){
15733         var fn = this.createFilterFn(property, value, anyMatch);
15734         return fn ? this.queryBy(fn) : this.data.clone();
15735     },
15736
15737     /**
15738      * Query by a function. The specified function will be called with each
15739      * record in this data source. If the function returns true the record is included
15740      * in the results.
15741      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15742      * @param {Object} scope (optional) The scope of the function (defaults to this)
15743       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15744      **/
15745     queryBy : function(fn, scope){
15746         var data = this.snapshot || this.data;
15747         return data.filterBy(fn, scope||this);
15748     },
15749
15750     /**
15751      * Collects unique values for a particular dataIndex from this store.
15752      * @param {String} dataIndex The property to collect
15753      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15754      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15755      * @return {Array} An array of the unique values
15756      **/
15757     collect : function(dataIndex, allowNull, bypassFilter){
15758         var d = (bypassFilter === true && this.snapshot) ?
15759                 this.snapshot.items : this.data.items;
15760         var v, sv, r = [], l = {};
15761         for(var i = 0, len = d.length; i < len; i++){
15762             v = d[i].data[dataIndex];
15763             sv = String(v);
15764             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15765                 l[sv] = true;
15766                 r[r.length] = v;
15767             }
15768         }
15769         return r;
15770     },
15771
15772     /**
15773      * Revert to a view of the Record cache with no filtering applied.
15774      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15775      */
15776     clearFilter : function(suppressEvent){
15777         if(this.snapshot && this.snapshot != this.data){
15778             this.data = this.snapshot;
15779             delete this.snapshot;
15780             if(suppressEvent !== true){
15781                 this.fireEvent("datachanged", this);
15782             }
15783         }
15784     },
15785
15786     // private
15787     afterEdit : function(record){
15788         if(this.modified.indexOf(record) == -1){
15789             this.modified.push(record);
15790         }
15791         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15792     },
15793     
15794     // private
15795     afterReject : function(record){
15796         this.modified.remove(record);
15797         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15798     },
15799
15800     // private
15801     afterCommit : function(record){
15802         this.modified.remove(record);
15803         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15804     },
15805
15806     /**
15807      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15808      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15809      */
15810     commitChanges : function(){
15811         var m = this.modified.slice(0);
15812         this.modified = [];
15813         for(var i = 0, len = m.length; i < len; i++){
15814             m[i].commit();
15815         }
15816     },
15817
15818     /**
15819      * Cancel outstanding changes on all changed records.
15820      */
15821     rejectChanges : function(){
15822         var m = this.modified.slice(0);
15823         this.modified = [];
15824         for(var i = 0, len = m.length; i < len; i++){
15825             m[i].reject();
15826         }
15827     },
15828
15829     onMetaChange : function(meta, rtype, o){
15830         this.recordType = rtype;
15831         this.fields = rtype.prototype.fields;
15832         delete this.snapshot;
15833         this.sortInfo = meta.sortInfo || this.sortInfo;
15834         this.modified = [];
15835         this.fireEvent('metachange', this, this.reader.meta);
15836     },
15837     
15838     moveIndex : function(data, type)
15839     {
15840         var index = this.indexOf(data);
15841         
15842         var newIndex = index + type;
15843         
15844         this.remove(data);
15845         
15846         this.insert(newIndex, data);
15847         
15848     }
15849 });/*
15850  * Based on:
15851  * Ext JS Library 1.1.1
15852  * Copyright(c) 2006-2007, Ext JS, LLC.
15853  *
15854  * Originally Released Under LGPL - original licence link has changed is not relivant.
15855  *
15856  * Fork - LGPL
15857  * <script type="text/javascript">
15858  */
15859
15860 /**
15861  * @class Roo.data.SimpleStore
15862  * @extends Roo.data.Store
15863  * Small helper class to make creating Stores from Array data easier.
15864  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15865  * @cfg {Array} fields An array of field definition objects, or field name strings.
15866  * @cfg {Object} an existing reader (eg. copied from another store)
15867  * @cfg {Array} data The multi-dimensional array of data
15868  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15869  * @cfg {Roo.data.Reader} reader  [not-required] 
15870  * @constructor
15871  * @param {Object} config
15872  */
15873 Roo.data.SimpleStore = function(config)
15874 {
15875     Roo.data.SimpleStore.superclass.constructor.call(this, {
15876         isLocal : true,
15877         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15878                 id: config.id
15879             },
15880             Roo.data.Record.create(config.fields)
15881         ),
15882         proxy : new Roo.data.MemoryProxy(config.data)
15883     });
15884     this.load();
15885 };
15886 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15887  * Based on:
15888  * Ext JS Library 1.1.1
15889  * Copyright(c) 2006-2007, Ext JS, LLC.
15890  *
15891  * Originally Released Under LGPL - original licence link has changed is not relivant.
15892  *
15893  * Fork - LGPL
15894  * <script type="text/javascript">
15895  */
15896
15897 /**
15898 /**
15899  * @extends Roo.data.Store
15900  * @class Roo.data.JsonStore
15901  * Small helper class to make creating Stores for JSON data easier. <br/>
15902 <pre><code>
15903 var store = new Roo.data.JsonStore({
15904     url: 'get-images.php',
15905     root: 'images',
15906     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15907 });
15908 </code></pre>
15909  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15910  * JsonReader and HttpProxy (unless inline data is provided).</b>
15911  * @cfg {Array} fields An array of field definition objects, or field name strings.
15912  * @constructor
15913  * @param {Object} config
15914  */
15915 Roo.data.JsonStore = function(c){
15916     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15917         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15918         reader: new Roo.data.JsonReader(c, c.fields)
15919     }));
15920 };
15921 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15922  * Based on:
15923  * Ext JS Library 1.1.1
15924  * Copyright(c) 2006-2007, Ext JS, LLC.
15925  *
15926  * Originally Released Under LGPL - original licence link has changed is not relivant.
15927  *
15928  * Fork - LGPL
15929  * <script type="text/javascript">
15930  */
15931
15932  
15933 Roo.data.Field = function(config){
15934     if(typeof config == "string"){
15935         config = {name: config};
15936     }
15937     Roo.apply(this, config);
15938     
15939     if(!this.type){
15940         this.type = "auto";
15941     }
15942     
15943     var st = Roo.data.SortTypes;
15944     // named sortTypes are supported, here we look them up
15945     if(typeof this.sortType == "string"){
15946         this.sortType = st[this.sortType];
15947     }
15948     
15949     // set default sortType for strings and dates
15950     if(!this.sortType){
15951         switch(this.type){
15952             case "string":
15953                 this.sortType = st.asUCString;
15954                 break;
15955             case "date":
15956                 this.sortType = st.asDate;
15957                 break;
15958             default:
15959                 this.sortType = st.none;
15960         }
15961     }
15962
15963     // define once
15964     var stripRe = /[\$,%]/g;
15965
15966     // prebuilt conversion function for this field, instead of
15967     // switching every time we're reading a value
15968     if(!this.convert){
15969         var cv, dateFormat = this.dateFormat;
15970         switch(this.type){
15971             case "":
15972             case "auto":
15973             case undefined:
15974                 cv = function(v){ return v; };
15975                 break;
15976             case "string":
15977                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15978                 break;
15979             case "int":
15980                 cv = function(v){
15981                     return v !== undefined && v !== null && v !== '' ?
15982                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15983                     };
15984                 break;
15985             case "float":
15986                 cv = function(v){
15987                     return v !== undefined && v !== null && v !== '' ?
15988                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15989                     };
15990                 break;
15991             case "bool":
15992             case "boolean":
15993                 cv = function(v){ return v === true || v === "true" || v == 1; };
15994                 break;
15995             case "date":
15996                 cv = function(v){
15997                     if(!v){
15998                         return '';
15999                     }
16000                     if(v instanceof Date){
16001                         return v;
16002                     }
16003                     if(dateFormat){
16004                         if(dateFormat == "timestamp"){
16005                             return new Date(v*1000);
16006                         }
16007                         return Date.parseDate(v, dateFormat);
16008                     }
16009                     var parsed = Date.parse(v);
16010                     return parsed ? new Date(parsed) : null;
16011                 };
16012              break;
16013             
16014         }
16015         this.convert = cv;
16016     }
16017 };
16018
16019 Roo.data.Field.prototype = {
16020     dateFormat: null,
16021     defaultValue: "",
16022     mapping: null,
16023     sortType : null,
16024     sortDir : "ASC"
16025 };/*
16026  * Based on:
16027  * Ext JS Library 1.1.1
16028  * Copyright(c) 2006-2007, Ext JS, LLC.
16029  *
16030  * Originally Released Under LGPL - original licence link has changed is not relivant.
16031  *
16032  * Fork - LGPL
16033  * <script type="text/javascript">
16034  */
16035  
16036 // Base class for reading structured data from a data source.  This class is intended to be
16037 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16038
16039 /**
16040  * @class Roo.data.DataReader
16041  * @abstract
16042  * Base class for reading structured data from a data source.  This class is intended to be
16043  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16044  */
16045
16046 Roo.data.DataReader = function(meta, recordType){
16047     
16048     this.meta = meta;
16049     
16050     this.recordType = recordType instanceof Array ? 
16051         Roo.data.Record.create(recordType) : recordType;
16052 };
16053
16054 Roo.data.DataReader.prototype = {
16055     
16056     
16057     readerType : 'Data',
16058      /**
16059      * Create an empty record
16060      * @param {Object} data (optional) - overlay some values
16061      * @return {Roo.data.Record} record created.
16062      */
16063     newRow :  function(d) {
16064         var da =  {};
16065         this.recordType.prototype.fields.each(function(c) {
16066             switch( c.type) {
16067                 case 'int' : da[c.name] = 0; break;
16068                 case 'date' : da[c.name] = new Date(); break;
16069                 case 'float' : da[c.name] = 0.0; break;
16070                 case 'boolean' : da[c.name] = false; break;
16071                 default : da[c.name] = ""; break;
16072             }
16073             
16074         });
16075         return new this.recordType(Roo.apply(da, d));
16076     }
16077     
16078     
16079 };/*
16080  * Based on:
16081  * Ext JS Library 1.1.1
16082  * Copyright(c) 2006-2007, Ext JS, LLC.
16083  *
16084  * Originally Released Under LGPL - original licence link has changed is not relivant.
16085  *
16086  * Fork - LGPL
16087  * <script type="text/javascript">
16088  */
16089
16090 /**
16091  * @class Roo.data.DataProxy
16092  * @extends Roo.util.Observable
16093  * @abstract
16094  * This class is an abstract base class for implementations which provide retrieval of
16095  * unformatted data objects.<br>
16096  * <p>
16097  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16098  * (of the appropriate type which knows how to parse the data object) to provide a block of
16099  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16100  * <p>
16101  * Custom implementations must implement the load method as described in
16102  * {@link Roo.data.HttpProxy#load}.
16103  */
16104 Roo.data.DataProxy = function(){
16105     this.addEvents({
16106         /**
16107          * @event beforeload
16108          * Fires before a network request is made to retrieve a data object.
16109          * @param {Object} This DataProxy object.
16110          * @param {Object} params The params parameter to the load function.
16111          */
16112         beforeload : true,
16113         /**
16114          * @event load
16115          * Fires before the load method's callback is called.
16116          * @param {Object} This DataProxy object.
16117          * @param {Object} o The data object.
16118          * @param {Object} arg The callback argument object passed to the load function.
16119          */
16120         load : true,
16121         /**
16122          * @event loadexception
16123          * Fires if an Exception occurs during data retrieval.
16124          * @param {Object} This DataProxy object.
16125          * @param {Object} o The data object.
16126          * @param {Object} arg The callback argument object passed to the load function.
16127          * @param {Object} e The Exception.
16128          */
16129         loadexception : true
16130     });
16131     Roo.data.DataProxy.superclass.constructor.call(this);
16132 };
16133
16134 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16135
16136     /**
16137      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16138      */
16139 /*
16140  * Based on:
16141  * Ext JS Library 1.1.1
16142  * Copyright(c) 2006-2007, Ext JS, LLC.
16143  *
16144  * Originally Released Under LGPL - original licence link has changed is not relivant.
16145  *
16146  * Fork - LGPL
16147  * <script type="text/javascript">
16148  */
16149 /**
16150  * @class Roo.data.MemoryProxy
16151  * @extends Roo.data.DataProxy
16152  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16153  * to the Reader when its load method is called.
16154  * @constructor
16155  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16156  */
16157 Roo.data.MemoryProxy = function(config){
16158     var data = config;
16159     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16160         data = config.data;
16161     }
16162     Roo.data.MemoryProxy.superclass.constructor.call(this);
16163     this.data = data;
16164 };
16165
16166 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16167     
16168     /**
16169      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16170      */
16171     /**
16172      * Load data from the requested source (in this case an in-memory
16173      * data object passed to the constructor), read the data object into
16174      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16175      * process that block using the passed callback.
16176      * @param {Object} params This parameter is not used by the MemoryProxy class.
16177      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16178      * object into a block of Roo.data.Records.
16179      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16180      * The function must be passed <ul>
16181      * <li>The Record block object</li>
16182      * <li>The "arg" argument from the load function</li>
16183      * <li>A boolean success indicator</li>
16184      * </ul>
16185      * @param {Object} scope The scope in which to call the callback
16186      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16187      */
16188     load : function(params, reader, callback, scope, arg){
16189         params = params || {};
16190         var result;
16191         try {
16192             result = reader.readRecords(params.data ? params.data :this.data);
16193         }catch(e){
16194             this.fireEvent("loadexception", this, arg, null, e);
16195             callback.call(scope, null, arg, false);
16196             return;
16197         }
16198         callback.call(scope, result, arg, true);
16199     },
16200     
16201     // private
16202     update : function(params, records){
16203         
16204     }
16205 });/*
16206  * Based on:
16207  * Ext JS Library 1.1.1
16208  * Copyright(c) 2006-2007, Ext JS, LLC.
16209  *
16210  * Originally Released Under LGPL - original licence link has changed is not relivant.
16211  *
16212  * Fork - LGPL
16213  * <script type="text/javascript">
16214  */
16215 /**
16216  * @class Roo.data.HttpProxy
16217  * @extends Roo.data.DataProxy
16218  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16219  * configured to reference a certain URL.<br><br>
16220  * <p>
16221  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16222  * from which the running page was served.<br><br>
16223  * <p>
16224  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16225  * <p>
16226  * Be aware that to enable the browser to parse an XML document, the server must set
16227  * the Content-Type header in the HTTP response to "text/xml".
16228  * @constructor
16229  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16230  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16231  * will be used to make the request.
16232  */
16233 Roo.data.HttpProxy = function(conn){
16234     Roo.data.HttpProxy.superclass.constructor.call(this);
16235     // is conn a conn config or a real conn?
16236     this.conn = conn;
16237     this.useAjax = !conn || !conn.events;
16238   
16239 };
16240
16241 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16242     // thse are take from connection...
16243     
16244     /**
16245      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
16246      */
16247     /**
16248      * @cfg {Object} extraParams  An object containing properties which are used as
16249      * extra parameters to each request made by this object. (defaults to undefined)
16250      */
16251     /**
16252      * @cfg {Object} defaultHeaders   An object containing request headers which are added
16253      *  to each request made by this object. (defaults to undefined)
16254      */
16255     /**
16256      * @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)
16257      */
16258     /**
16259      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16260      */
16261      /**
16262      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16263      * @type Boolean
16264      */
16265   
16266
16267     /**
16268      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16269      * @type Boolean
16270      */
16271     /**
16272      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16273      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16274      * a finer-grained basis than the DataProxy events.
16275      */
16276     getConnection : function(){
16277         return this.useAjax ? Roo.Ajax : this.conn;
16278     },
16279
16280     /**
16281      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16282      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16283      * process that block using the passed callback.
16284      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16285      * for the request to the remote server.
16286      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16287      * object into a block of Roo.data.Records.
16288      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16289      * The function must be passed <ul>
16290      * <li>The Record block object</li>
16291      * <li>The "arg" argument from the load function</li>
16292      * <li>A boolean success indicator</li>
16293      * </ul>
16294      * @param {Object} scope The scope in which to call the callback
16295      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16296      */
16297     load : function(params, reader, callback, scope, arg){
16298         if(this.fireEvent("beforeload", this, params) !== false){
16299             var  o = {
16300                 params : params || {},
16301                 request: {
16302                     callback : callback,
16303                     scope : scope,
16304                     arg : arg
16305                 },
16306                 reader: reader,
16307                 callback : this.loadResponse,
16308                 scope: this
16309             };
16310             if(this.useAjax){
16311                 Roo.applyIf(o, this.conn);
16312                 if(this.activeRequest){
16313                     Roo.Ajax.abort(this.activeRequest);
16314                 }
16315                 this.activeRequest = Roo.Ajax.request(o);
16316             }else{
16317                 this.conn.request(o);
16318             }
16319         }else{
16320             callback.call(scope||this, null, arg, false);
16321         }
16322     },
16323
16324     // private
16325     loadResponse : function(o, success, response){
16326         delete this.activeRequest;
16327         if(!success){
16328             this.fireEvent("loadexception", this, o, response);
16329             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16330             return;
16331         }
16332         var result;
16333         try {
16334             result = o.reader.read(response);
16335         }catch(e){
16336             o.success = false;
16337             o.raw = { errorMsg : response.responseText };
16338             this.fireEvent("loadexception", this, o, response, e);
16339             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16340             return;
16341         }
16342         
16343         this.fireEvent("load", this, o, o.request.arg);
16344         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16345     },
16346
16347     // private
16348     update : function(dataSet){
16349
16350     },
16351
16352     // private
16353     updateResponse : function(dataSet){
16354
16355     }
16356 });/*
16357  * Based on:
16358  * Ext JS Library 1.1.1
16359  * Copyright(c) 2006-2007, Ext JS, LLC.
16360  *
16361  * Originally Released Under LGPL - original licence link has changed is not relivant.
16362  *
16363  * Fork - LGPL
16364  * <script type="text/javascript">
16365  */
16366
16367 /**
16368  * @class Roo.data.ScriptTagProxy
16369  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16370  * other than the originating domain of the running page.<br><br>
16371  * <p>
16372  * <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
16373  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16374  * <p>
16375  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16376  * source code that is used as the source inside a &lt;script> tag.<br><br>
16377  * <p>
16378  * In order for the browser to process the returned data, the server must wrap the data object
16379  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16380  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16381  * depending on whether the callback name was passed:
16382  * <p>
16383  * <pre><code>
16384 boolean scriptTag = false;
16385 String cb = request.getParameter("callback");
16386 if (cb != null) {
16387     scriptTag = true;
16388     response.setContentType("text/javascript");
16389 } else {
16390     response.setContentType("application/x-json");
16391 }
16392 Writer out = response.getWriter();
16393 if (scriptTag) {
16394     out.write(cb + "(");
16395 }
16396 out.print(dataBlock.toJsonString());
16397 if (scriptTag) {
16398     out.write(");");
16399 }
16400 </pre></code>
16401  *
16402  * @constructor
16403  * @param {Object} config A configuration object.
16404  */
16405 Roo.data.ScriptTagProxy = function(config){
16406     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16407     Roo.apply(this, config);
16408     this.head = document.getElementsByTagName("head")[0];
16409 };
16410
16411 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16412
16413 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16414     /**
16415      * @cfg {String} url The URL from which to request the data object.
16416      */
16417     /**
16418      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16419      */
16420     timeout : 30000,
16421     /**
16422      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16423      * the server the name of the callback function set up by the load call to process the returned data object.
16424      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16425      * javascript output which calls this named function passing the data object as its only parameter.
16426      */
16427     callbackParam : "callback",
16428     /**
16429      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16430      * name to the request.
16431      */
16432     nocache : true,
16433
16434     /**
16435      * Load data from the configured URL, read the data object into
16436      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16437      * process that block using the passed callback.
16438      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16439      * for the request to the remote server.
16440      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16441      * object into a block of Roo.data.Records.
16442      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16443      * The function must be passed <ul>
16444      * <li>The Record block object</li>
16445      * <li>The "arg" argument from the load function</li>
16446      * <li>A boolean success indicator</li>
16447      * </ul>
16448      * @param {Object} scope The scope in which to call the callback
16449      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16450      */
16451     load : function(params, reader, callback, scope, arg){
16452         if(this.fireEvent("beforeload", this, params) !== false){
16453
16454             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16455
16456             var url = this.url;
16457             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16458             if(this.nocache){
16459                 url += "&_dc=" + (new Date().getTime());
16460             }
16461             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16462             var trans = {
16463                 id : transId,
16464                 cb : "stcCallback"+transId,
16465                 scriptId : "stcScript"+transId,
16466                 params : params,
16467                 arg : arg,
16468                 url : url,
16469                 callback : callback,
16470                 scope : scope,
16471                 reader : reader
16472             };
16473             var conn = this;
16474
16475             window[trans.cb] = function(o){
16476                 conn.handleResponse(o, trans);
16477             };
16478
16479             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16480
16481             if(this.autoAbort !== false){
16482                 this.abort();
16483             }
16484
16485             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16486
16487             var script = document.createElement("script");
16488             script.setAttribute("src", url);
16489             script.setAttribute("type", "text/javascript");
16490             script.setAttribute("id", trans.scriptId);
16491             this.head.appendChild(script);
16492
16493             this.trans = trans;
16494         }else{
16495             callback.call(scope||this, null, arg, false);
16496         }
16497     },
16498
16499     // private
16500     isLoading : function(){
16501         return this.trans ? true : false;
16502     },
16503
16504     /**
16505      * Abort the current server request.
16506      */
16507     abort : function(){
16508         if(this.isLoading()){
16509             this.destroyTrans(this.trans);
16510         }
16511     },
16512
16513     // private
16514     destroyTrans : function(trans, isLoaded){
16515         this.head.removeChild(document.getElementById(trans.scriptId));
16516         clearTimeout(trans.timeoutId);
16517         if(isLoaded){
16518             window[trans.cb] = undefined;
16519             try{
16520                 delete window[trans.cb];
16521             }catch(e){}
16522         }else{
16523             // if hasn't been loaded, wait for load to remove it to prevent script error
16524             window[trans.cb] = function(){
16525                 window[trans.cb] = undefined;
16526                 try{
16527                     delete window[trans.cb];
16528                 }catch(e){}
16529             };
16530         }
16531     },
16532
16533     // private
16534     handleResponse : function(o, trans){
16535         this.trans = false;
16536         this.destroyTrans(trans, true);
16537         var result;
16538         try {
16539             result = trans.reader.readRecords(o);
16540         }catch(e){
16541             this.fireEvent("loadexception", this, o, trans.arg, e);
16542             trans.callback.call(trans.scope||window, null, trans.arg, false);
16543             return;
16544         }
16545         this.fireEvent("load", this, o, trans.arg);
16546         trans.callback.call(trans.scope||window, result, trans.arg, true);
16547     },
16548
16549     // private
16550     handleFailure : function(trans){
16551         this.trans = false;
16552         this.destroyTrans(trans, false);
16553         this.fireEvent("loadexception", this, null, trans.arg);
16554         trans.callback.call(trans.scope||window, null, trans.arg, false);
16555     }
16556 });/*
16557  * Based on:
16558  * Ext JS Library 1.1.1
16559  * Copyright(c) 2006-2007, Ext JS, LLC.
16560  *
16561  * Originally Released Under LGPL - original licence link has changed is not relivant.
16562  *
16563  * Fork - LGPL
16564  * <script type="text/javascript">
16565  */
16566
16567 /**
16568  * @class Roo.data.JsonReader
16569  * @extends Roo.data.DataReader
16570  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16571  * based on mappings in a provided Roo.data.Record constructor.
16572  * 
16573  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16574  * in the reply previously. 
16575  * 
16576  * <p>
16577  * Example code:
16578  * <pre><code>
16579 var RecordDef = Roo.data.Record.create([
16580     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16581     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16582 ]);
16583 var myReader = new Roo.data.JsonReader({
16584     totalProperty: "results",    // The property which contains the total dataset size (optional)
16585     root: "rows",                // The property which contains an Array of row objects
16586     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16587 }, RecordDef);
16588 </code></pre>
16589  * <p>
16590  * This would consume a JSON file like this:
16591  * <pre><code>
16592 { 'results': 2, 'rows': [
16593     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16594     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16595 }
16596 </code></pre>
16597  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16598  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16599  * paged from the remote server.
16600  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16601  * @cfg {String} root name of the property which contains the Array of row objects.
16602  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16603  * @cfg {Array} fields Array of field definition objects
16604  * @constructor
16605  * Create a new JsonReader
16606  * @param {Object} meta Metadata configuration options
16607  * @param {Object} recordType Either an Array of field definition objects,
16608  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16609  */
16610 Roo.data.JsonReader = function(meta, recordType){
16611     
16612     meta = meta || {};
16613     // set some defaults:
16614     Roo.applyIf(meta, {
16615         totalProperty: 'total',
16616         successProperty : 'success',
16617         root : 'data',
16618         id : 'id'
16619     });
16620     
16621     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16622 };
16623 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16624     
16625     readerType : 'Json',
16626     
16627     /**
16628      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16629      * Used by Store query builder to append _requestMeta to params.
16630      * 
16631      */
16632     metaFromRemote : false,
16633     /**
16634      * This method is only used by a DataProxy which has retrieved data from a remote server.
16635      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16636      * @return {Object} data A data block which is used by an Roo.data.Store object as
16637      * a cache of Roo.data.Records.
16638      */
16639     read : function(response){
16640         var json = response.responseText;
16641        
16642         var o = /* eval:var:o */ eval("("+json+")");
16643         if(!o) {
16644             throw {message: "JsonReader.read: Json object not found"};
16645         }
16646         
16647         if(o.metaData){
16648             
16649             delete this.ef;
16650             this.metaFromRemote = true;
16651             this.meta = o.metaData;
16652             this.recordType = Roo.data.Record.create(o.metaData.fields);
16653             this.onMetaChange(this.meta, this.recordType, o);
16654         }
16655         return this.readRecords(o);
16656     },
16657
16658     // private function a store will implement
16659     onMetaChange : function(meta, recordType, o){
16660
16661     },
16662
16663     /**
16664          * @ignore
16665          */
16666     simpleAccess: function(obj, subsc) {
16667         return obj[subsc];
16668     },
16669
16670         /**
16671          * @ignore
16672          */
16673     getJsonAccessor: function(){
16674         var re = /[\[\.]/;
16675         return function(expr) {
16676             try {
16677                 return(re.test(expr))
16678                     ? new Function("obj", "return obj." + expr)
16679                     : function(obj){
16680                         return obj[expr];
16681                     };
16682             } catch(e){}
16683             return Roo.emptyFn;
16684         };
16685     }(),
16686
16687     /**
16688      * Create a data block containing Roo.data.Records from an XML document.
16689      * @param {Object} o An object which contains an Array of row objects in the property specified
16690      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16691      * which contains the total size of the dataset.
16692      * @return {Object} data A data block which is used by an Roo.data.Store object as
16693      * a cache of Roo.data.Records.
16694      */
16695     readRecords : function(o){
16696         /**
16697          * After any data loads, the raw JSON data is available for further custom processing.
16698          * @type Object
16699          */
16700         this.o = o;
16701         var s = this.meta, Record = this.recordType,
16702             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16703
16704 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16705         if (!this.ef) {
16706             if(s.totalProperty) {
16707                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16708                 }
16709                 if(s.successProperty) {
16710                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16711                 }
16712                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16713                 if (s.id) {
16714                         var g = this.getJsonAccessor(s.id);
16715                         this.getId = function(rec) {
16716                                 var r = g(rec);  
16717                                 return (r === undefined || r === "") ? null : r;
16718                         };
16719                 } else {
16720                         this.getId = function(){return null;};
16721                 }
16722             this.ef = [];
16723             for(var jj = 0; jj < fl; jj++){
16724                 f = fi[jj];
16725                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16726                 this.ef[jj] = this.getJsonAccessor(map);
16727             }
16728         }
16729
16730         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16731         if(s.totalProperty){
16732             var vt = parseInt(this.getTotal(o), 10);
16733             if(!isNaN(vt)){
16734                 totalRecords = vt;
16735             }
16736         }
16737         if(s.successProperty){
16738             var vs = this.getSuccess(o);
16739             if(vs === false || vs === 'false'){
16740                 success = false;
16741             }
16742         }
16743         var records = [];
16744         for(var i = 0; i < c; i++){
16745             var n = root[i];
16746             var values = {};
16747             var id = this.getId(n);
16748             for(var j = 0; j < fl; j++){
16749                 f = fi[j];
16750                                 var v = this.ef[j](n);
16751                                 if (!f.convert) {
16752                                         Roo.log('missing convert for ' + f.name);
16753                                         Roo.log(f);
16754                                         continue;
16755                                 }
16756                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16757             }
16758                         if (!Record) {
16759                                 return {
16760                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16761                                         success : false,
16762                                         records : [],
16763                                         totalRecords : 0
16764                                 };
16765                         }
16766             var record = new Record(values, id);
16767             record.json = n;
16768             records[i] = record;
16769         }
16770         return {
16771             raw : o,
16772             success : success,
16773             records : records,
16774             totalRecords : totalRecords
16775         };
16776     },
16777     // used when loading children.. @see loadDataFromChildren
16778     toLoadData: function(rec)
16779     {
16780         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16781         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16782         return { data : data, total : data.length };
16783         
16784     }
16785 });/*
16786  * Based on:
16787  * Ext JS Library 1.1.1
16788  * Copyright(c) 2006-2007, Ext JS, LLC.
16789  *
16790  * Originally Released Under LGPL - original licence link has changed is not relivant.
16791  *
16792  * Fork - LGPL
16793  * <script type="text/javascript">
16794  */
16795
16796 /**
16797  * @class Roo.data.ArrayReader
16798  * @extends Roo.data.DataReader
16799  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16800  * Each element of that Array represents a row of data fields. The
16801  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16802  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16803  * <p>
16804  * Example code:.
16805  * <pre><code>
16806 var RecordDef = Roo.data.Record.create([
16807     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16808     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16809 ]);
16810 var myReader = new Roo.data.ArrayReader({
16811     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16812 }, RecordDef);
16813 </code></pre>
16814  * <p>
16815  * This would consume an Array like this:
16816  * <pre><code>
16817 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16818   </code></pre>
16819  
16820  * @constructor
16821  * Create a new JsonReader
16822  * @param {Object} meta Metadata configuration options.
16823  * @param {Object|Array} recordType Either an Array of field definition objects
16824  * 
16825  * @cfg {Array} fields Array of field definition objects
16826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16827  * as specified to {@link Roo.data.Record#create},
16828  * or an {@link Roo.data.Record} object
16829  *
16830  * 
16831  * created using {@link Roo.data.Record#create}.
16832  */
16833 Roo.data.ArrayReader = function(meta, recordType)
16834 {    
16835     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16836 };
16837
16838 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16839     
16840       /**
16841      * Create a data block containing Roo.data.Records from an XML document.
16842      * @param {Object} o An Array of row objects which represents the dataset.
16843      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16844      * a cache of Roo.data.Records.
16845      */
16846     readRecords : function(o)
16847     {
16848         var sid = this.meta ? this.meta.id : null;
16849         var recordType = this.recordType, fields = recordType.prototype.fields;
16850         var records = [];
16851         var root = o;
16852         for(var i = 0; i < root.length; i++){
16853             var n = root[i];
16854             var values = {};
16855             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16856             for(var j = 0, jlen = fields.length; j < jlen; j++){
16857                 var f = fields.items[j];
16858                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16859                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16860                 v = f.convert(v);
16861                 values[f.name] = v;
16862             }
16863             var record = new recordType(values, id);
16864             record.json = n;
16865             records[records.length] = record;
16866         }
16867         return {
16868             records : records,
16869             totalRecords : records.length
16870         };
16871     },
16872     // used when loading children.. @see loadDataFromChildren
16873     toLoadData: function(rec)
16874     {
16875         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16876         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16877         
16878     }
16879     
16880     
16881 });/*
16882  * - LGPL
16883  * * 
16884  */
16885
16886 /**
16887  * @class Roo.bootstrap.form.ComboBox
16888  * @extends Roo.bootstrap.form.TriggerField
16889  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16890  * @cfg {Boolean} append (true|false) default false
16891  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16892  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16893  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16894  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16895  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16896  * @cfg {Boolean} animate default true
16897  * @cfg {Boolean} emptyResultText only for touch device
16898  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16899  * @cfg {String} emptyTitle default ''
16900  * @cfg {Number} width fixed with? experimental
16901  * @constructor
16902  * Create a new ComboBox.
16903  * @param {Object} config Configuration options
16904  */
16905 Roo.bootstrap.form.ComboBox = function(config){
16906     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16907     this.addEvents({
16908         /**
16909          * @event expand
16910          * Fires when the dropdown list is expanded
16911         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16912         */
16913         'expand' : true,
16914         /**
16915          * @event collapse
16916          * Fires when the dropdown list is collapsed
16917         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16918         */
16919         'collapse' : true,
16920         /**
16921          * @event beforeselect
16922          * Fires before a list item is selected. Return false to cancel the selection.
16923         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16924         * @param {Roo.data.Record} record The data record returned from the underlying store
16925         * @param {Number} index The index of the selected item in the dropdown list
16926         */
16927         'beforeselect' : true,
16928         /**
16929          * @event select
16930          * Fires when a list item is selected
16931         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16932         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16933         * @param {Number} index The index of the selected item in the dropdown list
16934         */
16935         'select' : true,
16936         /**
16937          * @event beforequery
16938          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16939          * The event object passed has these properties:
16940         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16941         * @param {String} query The query
16942         * @param {Boolean} forceAll true to force "all" query
16943         * @param {Boolean} cancel true to cancel the query
16944         * @param {Object} e The query event object
16945         */
16946         'beforequery': true,
16947          /**
16948          * @event add
16949          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16950         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16951         */
16952         'add' : true,
16953         /**
16954          * @event edit
16955          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16956         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16957         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16958         */
16959         'edit' : true,
16960         /**
16961          * @event remove
16962          * Fires when the remove value from the combobox array
16963         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16964         */
16965         'remove' : true,
16966         /**
16967          * @event afterremove
16968          * Fires when the remove value from the combobox array
16969         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16970         */
16971         'afterremove' : true,
16972         /**
16973          * @event specialfilter
16974          * Fires when specialfilter
16975             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16976             */
16977         'specialfilter' : true,
16978         /**
16979          * @event tick
16980          * Fires when tick the element
16981             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16982             */
16983         'tick' : true,
16984         /**
16985          * @event touchviewdisplay
16986          * Fires when touch view require special display (default is using displayField)
16987             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16988             * @param {Object} cfg set html .
16989             */
16990         'touchviewdisplay' : true
16991         
16992     });
16993     
16994     this.item = [];
16995     this.tickItems = [];
16996     
16997     this.selectedIndex = -1;
16998     if(this.mode == 'local'){
16999         if(config.queryDelay === undefined){
17000             this.queryDelay = 10;
17001         }
17002         if(config.minChars === undefined){
17003             this.minChars = 0;
17004         }
17005     }
17006 };
17007
17008 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
17009      
17010     /**
17011      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
17012      * rendering into an Roo.Editor, defaults to false)
17013      */
17014     /**
17015      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
17016      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
17017      */
17018     /**
17019      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
17020      */
17021     /**
17022      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
17023      * the dropdown list (defaults to undefined, with no header element)
17024      */
17025
17026      /**
17027      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
17028      */
17029      
17030      /**
17031      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17032      */
17033     listWidth: undefined,
17034     /**
17035      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17036      * mode = 'remote' or 'text' if mode = 'local')
17037      */
17038     displayField: undefined,
17039     
17040     /**
17041      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17042      * mode = 'remote' or 'value' if mode = 'local'). 
17043      * Note: use of a valueField requires the user make a selection
17044      * in order for a value to be mapped.
17045      */
17046     valueField: undefined,
17047     /**
17048      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17049      */
17050     modalTitle : '',
17051     
17052     /**
17053      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17054      * field's data value (defaults to the underlying DOM element's name)
17055      */
17056     hiddenName: undefined,
17057     /**
17058      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17059      */
17060     listClass: '',
17061     /**
17062      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17063      */
17064     selectedClass: 'active',
17065     
17066     /**
17067      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17068      */
17069     shadow:'sides',
17070     /**
17071      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17072      * anchor positions (defaults to 'tl-bl')
17073      */
17074     listAlign: 'tl-bl?',
17075     /**
17076      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17077      */
17078     // maxHeight: 300, // not used (change maxHeight in CSS. target the list using listClass)
17079     /**
17080      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17081      * query specified by the allQuery config option (defaults to 'query')
17082      */
17083     triggerAction: 'query',
17084     /**
17085      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17086      * (defaults to 4, does not apply if editable = false)
17087      */
17088     minChars : 4,
17089     /**
17090      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17091      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17092      */
17093     typeAhead: false,
17094     /**
17095      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17096      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17097      */
17098     queryDelay: 500,
17099     /**
17100      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17101      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17102      */
17103     pageSize: 0,
17104     /**
17105      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17106      * when editable = true (defaults to false)
17107      */
17108     selectOnFocus:false,
17109     /**
17110      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17111      */
17112     queryParam: 'query',
17113     /**
17114      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17115      * when mode = 'remote' (defaults to 'Loading...')
17116      */
17117     loadingText: 'Loading...',
17118     /**
17119      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17120      */
17121     resizable: false,
17122     /**
17123      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17124      */
17125     handleHeight : 8,
17126     /**
17127      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17128      * traditional select (defaults to true)
17129      */
17130     editable: true,
17131     /**
17132      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17133      */
17134     allQuery: '',
17135     /**
17136      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17137      */
17138     mode: 'remote',
17139     /**
17140      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17141      * listWidth has a higher value)
17142      */
17143     minListWidth : 70,
17144     /**
17145      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17146      * allow the user to set arbitrary text into the field (defaults to false)
17147      */
17148     forceSelection:false,
17149     /**
17150      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17151      * if typeAhead = true (defaults to 250)
17152      */
17153     typeAheadDelay : 250,
17154     /**
17155      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17156      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17157      */
17158     valueNotFoundText : undefined,
17159     /**
17160      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17161      */
17162     blockFocus : false,
17163     
17164     /**
17165      * @cfg {Boolean} disableClear Disable showing of clear button.
17166      */
17167     disableClear : false,
17168     /**
17169      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17170      */
17171     alwaysQuery : false,
17172     
17173     /**
17174      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17175      */
17176     multiple : false,
17177     
17178     /**
17179      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17180      */
17181     invalidClass : "has-warning",
17182     
17183     /**
17184      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17185      */
17186     validClass : "has-success",
17187     
17188     /**
17189      * @cfg {Boolean} specialFilter (true|false) special filter default false
17190      */
17191     specialFilter : false,
17192     
17193     /**
17194      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17195      */
17196     mobileTouchView : true,
17197     
17198     /**
17199      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17200      */
17201     useNativeIOS : false,
17202     
17203     /**
17204      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17205      */
17206     mobile_restrict_height : false,
17207     
17208     ios_options : false,
17209     
17210     //private
17211     addicon : false,
17212     editicon: false,
17213     
17214     page: 0,
17215     hasQuery: false,
17216     append: false,
17217     loadNext: false,
17218     autoFocus : true,
17219     tickable : false,
17220     btnPosition : 'right',
17221     triggerList : true,
17222     showToggleBtn : true,
17223     animate : true,
17224     emptyResultText: 'Empty',
17225     triggerText : 'Select',
17226     emptyTitle : '',
17227     width : false,
17228     
17229     // element that contains real text value.. (when hidden is used..)
17230     
17231     getAutoCreate : function()
17232     {   
17233         var cfg = false;
17234         //render
17235         /*
17236          * Render classic select for iso
17237          */
17238         
17239         if(Roo.isIOS && this.useNativeIOS){
17240             cfg = this.getAutoCreateNativeIOS();
17241             return cfg;
17242         }
17243         
17244         /*
17245          * Touch Devices
17246          */
17247         
17248         if(Roo.isTouch && this.mobileTouchView){
17249             cfg = this.getAutoCreateTouchView();
17250             return cfg;;
17251         }
17252         
17253         /*
17254          *  Normal ComboBox
17255          */
17256         if(!this.tickable){
17257             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17258             return cfg;
17259         }
17260         
17261         /*
17262          *  ComboBox with tickable selections
17263          */
17264              
17265         var align = this.labelAlign || this.parentLabelAlign();
17266         
17267         cfg = {
17268             cls : 'form-group roo-combobox-tickable' //input-group
17269         };
17270         
17271         var btn_text_select = '';
17272         var btn_text_done = '';
17273         var btn_text_cancel = '';
17274         
17275         if (this.btn_text_show) {
17276             btn_text_select = 'Select';
17277             btn_text_done = 'Done';
17278             btn_text_cancel = 'Cancel'; 
17279         }
17280         
17281         var buttons = {
17282             tag : 'div',
17283             cls : 'tickable-buttons',
17284             cn : [
17285                 {
17286                     tag : 'button',
17287                     type : 'button',
17288                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17289                     //html : this.triggerText
17290                     html: btn_text_select
17291                 },
17292                 {
17293                     tag : 'button',
17294                     type : 'button',
17295                     name : 'ok',
17296                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17297                     //html : 'Done'
17298                     html: btn_text_done
17299                 },
17300                 {
17301                     tag : 'button',
17302                     type : 'button',
17303                     name : 'cancel',
17304                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17305                     //html : 'Cancel'
17306                     html: btn_text_cancel
17307                 }
17308             ]
17309         };
17310         
17311         if(this.editable){
17312             buttons.cn.unshift({
17313                 tag: 'input',
17314                 cls: 'roo-select2-search-field-input'
17315             });
17316         }
17317         
17318         var _this = this;
17319         
17320         Roo.each(buttons.cn, function(c){
17321             if (_this.size) {
17322                 c.cls += ' btn-' + _this.size;
17323             }
17324
17325             if (_this.disabled) {
17326                 c.disabled = true;
17327             }
17328         });
17329         
17330         var box = {
17331             tag: 'div',
17332             style : 'display: contents',
17333             cn: [
17334                 {
17335                     tag: 'input',
17336                     type : 'hidden',
17337                     cls: 'form-hidden-field'
17338                 },
17339                 {
17340                     tag: 'ul',
17341                     cls: 'roo-select2-choices',
17342                     cn:[
17343                         {
17344                             tag: 'li',
17345                             cls: 'roo-select2-search-field',
17346                             cn: [
17347                                 buttons
17348                             ]
17349                         }
17350                     ]
17351                 }
17352             ]
17353         };
17354         
17355         var combobox = {
17356             cls: 'roo-select2-container input-group roo-select2-container-multi',
17357             cn: [
17358                 
17359                 box
17360 //                {
17361 //                    tag: 'ul',
17362 //                    cls: 'typeahead typeahead-long dropdown-menu',
17363 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17364 //                }
17365             ]
17366         };
17367         
17368         if(this.hasFeedback && !this.allowBlank){
17369             
17370             var feedback = {
17371                 tag: 'span',
17372                 cls: 'glyphicon form-control-feedback'
17373             };
17374
17375             combobox.cn.push(feedback);
17376         }
17377         
17378         
17379         
17380         var indicator = {
17381             tag : 'i',
17382             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17383             tooltip : 'This field is required'
17384         };
17385          
17386         if (this.allowBlank) {
17387             indicator = {
17388                 tag : 'i',
17389                 style : 'display:none'
17390             };
17391         } 
17392         if (align ==='left' && this.fieldLabel.length) {
17393             
17394             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17395             
17396             cfg.cn = [
17397                 indicator,
17398                 {
17399                     tag: 'label',
17400                     'for' :  id,
17401                     cls : 'control-label col-form-label',
17402                     html : this.fieldLabel
17403
17404                 },
17405                 {
17406                     cls : "", 
17407                     cn: [
17408                         combobox
17409                     ]
17410                 }
17411
17412             ];
17413             
17414             var labelCfg = cfg.cn[1];
17415             var contentCfg = cfg.cn[2];
17416             
17417
17418             if(this.indicatorpos == 'right'){
17419                 
17420                 cfg.cn = [
17421                     {
17422                         tag: 'label',
17423                         'for' :  id,
17424                         cls : 'control-label col-form-label',
17425                         cn : [
17426                             {
17427                                 tag : 'span',
17428                                 html : this.fieldLabel
17429                             },
17430                             indicator
17431                         ]
17432                     },
17433                     {
17434                         cls : "",
17435                         cn: [
17436                             combobox
17437                         ]
17438                     }
17439
17440                 ];
17441                 
17442                 
17443                 
17444                 labelCfg = cfg.cn[0];
17445                 contentCfg = cfg.cn[1];
17446             
17447             }
17448             
17449             if(this.labelWidth > 12){
17450                 labelCfg.style = "width: " + this.labelWidth + 'px';
17451             }
17452             if(this.width * 1 > 0){
17453                 contentCfg.style = "width: " + this.width + 'px';
17454             }
17455             if(this.labelWidth < 13 && this.labelmd == 0){
17456                 this.labelmd = this.labelWidth;
17457             }
17458             
17459             if(this.labellg > 0){
17460                 labelCfg.cls += ' col-lg-' + this.labellg;
17461                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17462             }
17463             
17464             if(this.labelmd > 0){
17465                 labelCfg.cls += ' col-md-' + this.labelmd;
17466                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17467             }
17468             
17469             if(this.labelsm > 0){
17470                 labelCfg.cls += ' col-sm-' + this.labelsm;
17471                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17472             }
17473             
17474             if(this.labelxs > 0){
17475                 labelCfg.cls += ' col-xs-' + this.labelxs;
17476                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17477             }
17478                 
17479                 
17480         } else if ( this.fieldLabel.length) {
17481 //                Roo.log(" label");
17482                  cfg.cn = [
17483                    indicator,
17484                     {
17485                         tag: 'label',
17486                         //cls : 'input-group-addon',
17487                         html : this.fieldLabel
17488                     },
17489                     combobox
17490                 ];
17491                 
17492                 if(this.indicatorpos == 'right'){
17493                     cfg.cn = [
17494                         {
17495                             tag: 'label',
17496                             //cls : 'input-group-addon',
17497                             html : this.fieldLabel
17498                         },
17499                         indicator,
17500                         combobox
17501                     ];
17502                     
17503                 }
17504
17505         } else {
17506             
17507 //                Roo.log(" no label && no align");
17508                 cfg = combobox
17509                      
17510                 
17511         }
17512          
17513         var settings=this;
17514         ['xs','sm','md','lg'].map(function(size){
17515             if (settings[size]) {
17516                 cfg.cls += ' col-' + size + '-' + settings[size];
17517             }
17518         });
17519         
17520         return cfg;
17521         
17522     },
17523     
17524     _initEventsCalled : false,
17525     
17526     // private
17527     initEvents: function()
17528     {   
17529         if (this._initEventsCalled) { // as we call render... prevent looping...
17530             return;
17531         }
17532         this._initEventsCalled = true;
17533         
17534         if (!this.store) {
17535             throw "can not find store for combo";
17536         }
17537         
17538         this.indicator = this.indicatorEl();
17539         
17540         this.store = Roo.factory(this.store, Roo.data);
17541         this.store.parent = this;
17542         
17543         // if we are building from html. then this element is so complex, that we can not really
17544         // use the rendered HTML.
17545         // so we have to trash and replace the previous code.
17546         if (Roo.XComponent.build_from_html) {
17547             // remove this element....
17548             var e = this.el.dom, k=0;
17549             while (e ) { e = e.previousSibling;  ++k;}
17550
17551             this.el.remove();
17552             
17553             this.el=false;
17554             this.rendered = false;
17555             
17556             this.render(this.parent().getChildContainer(true), k);
17557         }
17558         
17559         if(Roo.isIOS && this.useNativeIOS){
17560             this.initIOSView();
17561             return;
17562         }
17563         
17564         /*
17565          * Touch Devices
17566          */
17567         
17568         if(Roo.isTouch && this.mobileTouchView){
17569             this.initTouchView();
17570             return;
17571         }
17572         
17573         if(this.tickable){
17574             this.initTickableEvents();
17575             return;
17576         }
17577         
17578         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17579         
17580         if(this.hiddenName){
17581             
17582             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17583             
17584             this.hiddenField.dom.value =
17585                 this.hiddenValue !== undefined ? this.hiddenValue :
17586                 this.value !== undefined ? this.value : '';
17587
17588             // prevent input submission
17589             this.el.dom.removeAttribute('name');
17590             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17591              
17592              
17593         }
17594         //if(Roo.isGecko){
17595         //    this.el.dom.setAttribute('autocomplete', 'off');
17596         //}
17597         
17598         var cls = 'x-combo-list';
17599         
17600         //this.list = new Roo.Layer({
17601         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17602         //});
17603
17604         this.list.addClass(this.listClass);
17605         
17606         var _this = this;
17607         
17608         (function(){
17609             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17610             _this.list.setWidth(lw);
17611         }).defer(100);
17612         
17613         this.list.on('mouseover', this.onViewOver, this);
17614         this.list.on('mousemove', this.onViewMove, this);
17615         this.list.on('scroll', this.onViewScroll, this);
17616         
17617         /*
17618         this.list.swallowEvent('mousewheel');
17619         this.assetHeight = 0;
17620
17621         if(this.title){
17622             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17623             this.assetHeight += this.header.getHeight();
17624         }
17625
17626         this.innerList = this.list.createChild({cls:cls+'-inner'});
17627         this.innerList.on('mouseover', this.onViewOver, this);
17628         this.innerList.on('mousemove', this.onViewMove, this);
17629         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17630         
17631         if(this.allowBlank && !this.pageSize && !this.disableClear){
17632             this.footer = this.list.createChild({cls:cls+'-ft'});
17633             this.pageTb = new Roo.Toolbar(this.footer);
17634            
17635         }
17636         if(this.pageSize){
17637             this.footer = this.list.createChild({cls:cls+'-ft'});
17638             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17639                     {pageSize: this.pageSize});
17640             
17641         }
17642         
17643         if (this.pageTb && this.allowBlank && !this.disableClear) {
17644             var _this = this;
17645             this.pageTb.add(new Roo.Toolbar.Fill(), {
17646                 cls: 'x-btn-icon x-btn-clear',
17647                 text: '&#160;',
17648                 handler: function()
17649                 {
17650                     _this.collapse();
17651                     _this.clearValue();
17652                     _this.onSelect(false, -1);
17653                 }
17654             });
17655         }
17656         if (this.footer) {
17657             this.assetHeight += this.footer.getHeight();
17658         }
17659         */
17660             
17661         if(!this.tpl){
17662             this.tpl = Roo.bootstrap.version == 4 ?
17663                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17664                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17665         }
17666
17667         this.view = new Roo.View(this.list, this.tpl, {
17668             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17669         });
17670         //this.view.wrapEl.setDisplayed(false);
17671         this.view.on('click', this.onViewClick, this);
17672         
17673         
17674         this.store.on('beforeload', this.onBeforeLoad, this);
17675         this.store.on('load', this.onLoad, this);
17676         this.store.on('loadexception', this.onLoadException, this);
17677         /*
17678         if(this.resizable){
17679             this.resizer = new Roo.Resizable(this.list,  {
17680                pinned:true, handles:'se'
17681             });
17682             this.resizer.on('resize', function(r, w, h){
17683                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17684                 this.listWidth = w;
17685                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17686                 this.restrictHeight();
17687             }, this);
17688             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17689         }
17690         */
17691         if(!this.editable){
17692             this.editable = true;
17693             this.setEditable(false);
17694         }
17695         
17696         /*
17697         
17698         if (typeof(this.events.add.listeners) != 'undefined') {
17699             
17700             this.addicon = this.wrap.createChild(
17701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17702        
17703             this.addicon.on('click', function(e) {
17704                 this.fireEvent('add', this);
17705             }, this);
17706         }
17707         if (typeof(this.events.edit.listeners) != 'undefined') {
17708             
17709             this.editicon = this.wrap.createChild(
17710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17711             if (this.addicon) {
17712                 this.editicon.setStyle('margin-left', '40px');
17713             }
17714             this.editicon.on('click', function(e) {
17715                 
17716                 // we fire even  if inothing is selected..
17717                 this.fireEvent('edit', this, this.lastData );
17718                 
17719             }, this);
17720         }
17721         */
17722         
17723         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17724             "up" : function(e){
17725                 this.inKeyMode = true;
17726                 this.selectPrev();
17727             },
17728
17729             "down" : function(e){
17730                 if(!this.isExpanded()){
17731                     this.onTriggerClick();
17732                 }else{
17733                     this.inKeyMode = true;
17734                     this.selectNext();
17735                 }
17736             },
17737
17738             "enter" : function(e){
17739 //                this.onViewClick();
17740                 //return true;
17741                 this.collapse();
17742                 
17743                 if(this.fireEvent("specialkey", this, e)){
17744                     this.onViewClick(false);
17745                 }
17746                 
17747                 return true;
17748             },
17749
17750             "esc" : function(e){
17751                 this.collapse();
17752             },
17753
17754             "tab" : function(e){
17755                 this.collapse();
17756                 
17757                 if(this.fireEvent("specialkey", this, e)){
17758                     this.onViewClick(false);
17759                 }
17760                 
17761                 return true;
17762             },
17763
17764             scope : this,
17765
17766             doRelay : function(foo, bar, hname){
17767                 if(hname == 'down' || this.scope.isExpanded()){
17768                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17769                 }
17770                 return true;
17771             },
17772
17773             forceKeyDown: true
17774         });
17775         
17776         
17777         this.queryDelay = Math.max(this.queryDelay || 10,
17778                 this.mode == 'local' ? 10 : 250);
17779         
17780         
17781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17782         
17783         if(this.typeAhead){
17784             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17785         }
17786         if(this.editable !== false){
17787             this.inputEl().on("keyup", this.onKeyUp, this);
17788         }
17789         if(this.forceSelection){
17790             this.inputEl().on('blur', this.doForce, this);
17791         }
17792         
17793         if(this.multiple){
17794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17796         }
17797     },
17798     
17799     initTickableEvents: function()
17800     {   
17801         this.createList();
17802         
17803         if(this.hiddenName){
17804             
17805             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17806             
17807             this.hiddenField.dom.value =
17808                 this.hiddenValue !== undefined ? this.hiddenValue :
17809                 this.value !== undefined ? this.value : '';
17810
17811             // prevent input submission
17812             this.el.dom.removeAttribute('name');
17813             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17814              
17815              
17816         }
17817         
17818 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17819         
17820         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17821         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17822         if(this.triggerList){
17823             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17824         }
17825          
17826         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17827         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17828         
17829         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17830         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17831         
17832         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17833         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17834         
17835         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17836         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17837         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17838         
17839         this.okBtn.hide();
17840         this.cancelBtn.hide();
17841         
17842         var _this = this;
17843         
17844         (function(){
17845             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17846             _this.list.setWidth(lw);
17847         }).defer(100);
17848         
17849         this.list.on('mouseover', this.onViewOver, this);
17850         this.list.on('mousemove', this.onViewMove, this);
17851         
17852         this.list.on('scroll', this.onViewScroll, this);
17853         
17854         if(!this.tpl){
17855             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17856                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17857         }
17858
17859         this.view = new Roo.View(this.list, this.tpl, {
17860             singleSelect:true,
17861             tickable:true,
17862             parent:this,
17863             store: this.store,
17864             selectedClass: this.selectedClass
17865         });
17866         
17867         //this.view.wrapEl.setDisplayed(false);
17868         this.view.on('click', this.onViewClick, this);
17869         
17870         
17871         
17872         this.store.on('beforeload', this.onBeforeLoad, this);
17873         this.store.on('load', this.onLoad, this);
17874         this.store.on('loadexception', this.onLoadException, this);
17875         
17876         if(this.editable){
17877             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17878                 "up" : function(e){
17879                     this.inKeyMode = true;
17880                     this.selectPrev();
17881                 },
17882
17883                 "down" : function(e){
17884                     this.inKeyMode = true;
17885                     this.selectNext();
17886                 },
17887
17888                 "enter" : function(e){
17889                     if(this.fireEvent("specialkey", this, e)){
17890                         this.onViewClick(false);
17891                     }
17892                     
17893                     return true;
17894                 },
17895
17896                 "esc" : function(e){
17897                     this.onTickableFooterButtonClick(e, false, false);
17898                 },
17899
17900                 "tab" : function(e){
17901                     this.fireEvent("specialkey", this, e);
17902                     
17903                     this.onTickableFooterButtonClick(e, false, false);
17904                     
17905                     return true;
17906                 },
17907
17908                 scope : this,
17909
17910                 doRelay : function(e, fn, key){
17911                     if(this.scope.isExpanded()){
17912                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17913                     }
17914                     return true;
17915                 },
17916
17917                 forceKeyDown: true
17918             });
17919         }
17920         
17921         this.queryDelay = Math.max(this.queryDelay || 10,
17922                 this.mode == 'local' ? 10 : 250);
17923         
17924         
17925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17926         
17927         if(this.typeAhead){
17928             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17929         }
17930         
17931         if(this.editable !== false){
17932             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17933         }
17934         
17935         this.indicator = this.indicatorEl();
17936         
17937         if(this.indicator){
17938             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17939             this.indicator.hide();
17940         }
17941         
17942     },
17943
17944     onDestroy : function(){
17945         if(this.view){
17946             this.view.setStore(null);
17947             this.view.el.removeAllListeners();
17948             this.view.el.remove();
17949             this.view.purgeListeners();
17950         }
17951         if(this.list){
17952             this.list.dom.innerHTML  = '';
17953         }
17954         
17955         if(this.store){
17956             this.store.un('beforeload', this.onBeforeLoad, this);
17957             this.store.un('load', this.onLoad, this);
17958             this.store.un('loadexception', this.onLoadException, this);
17959         }
17960         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17961     },
17962
17963     // private
17964     fireKey : function(e){
17965         if(e.isNavKeyPress() && !this.list.isVisible()){
17966             this.fireEvent("specialkey", this, e);
17967         }
17968     },
17969
17970     // private
17971     onResize: function(w, h)
17972     {
17973         
17974         
17975 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17976 //        
17977 //        if(typeof w != 'number'){
17978 //            // we do not handle it!?!?
17979 //            return;
17980 //        }
17981 //        var tw = this.trigger.getWidth();
17982 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17983 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17984 //        var x = w - tw;
17985 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17986 //            
17987 //        //this.trigger.setStyle('left', x+'px');
17988 //        
17989 //        if(this.list && this.listWidth === undefined){
17990 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17991 //            this.list.setWidth(lw);
17992 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17993 //        }
17994         
17995     
17996         
17997     },
17998
17999     /**
18000      * Allow or prevent the user from directly editing the field text.  If false is passed,
18001      * the user will only be able to select from the items defined in the dropdown list.  This method
18002      * is the runtime equivalent of setting the 'editable' config option at config time.
18003      * @param {Boolean} value True to allow the user to directly edit the field text
18004      */
18005     setEditable : function(value){
18006         if(value == this.editable){
18007             return;
18008         }
18009         this.editable = value;
18010         if(!value){
18011             this.inputEl().dom.setAttribute('readOnly', true);
18012             this.inputEl().on('mousedown', this.onTriggerClick,  this);
18013             this.inputEl().addClass('x-combo-noedit');
18014         }else{
18015             this.inputEl().dom.removeAttribute('readOnly');
18016             this.inputEl().un('mousedown', this.onTriggerClick,  this);
18017             this.inputEl().removeClass('x-combo-noedit');
18018         }
18019     },
18020
18021     // private
18022     
18023     onBeforeLoad : function(combo,opts){
18024         if(!this.hasFocus){
18025             return;
18026         }
18027          if (!opts.add) {
18028             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18029          }
18030         this.restrictHeight();
18031         this.selectedIndex = -1;
18032     },
18033
18034     // private
18035     onLoad : function(){
18036         
18037         this.hasQuery = false;
18038         
18039         if(!this.hasFocus){
18040             return;
18041         }
18042         
18043         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18044             this.loading.hide();
18045         }
18046         
18047         if(this.store.getCount() > 0){
18048             
18049             this.expand();
18050             this.restrictHeight();
18051             if(this.lastQuery == this.allQuery){
18052                 if(this.editable && !this.tickable){
18053                     this.inputEl().dom.select();
18054                 }
18055                 
18056                 if(
18057                     !this.selectByValue(this.value, true) &&
18058                     this.autoFocus && 
18059                     (
18060                         !this.store.lastOptions ||
18061                         typeof(this.store.lastOptions.add) == 'undefined' || 
18062                         this.store.lastOptions.add != true
18063                     )
18064                 ){
18065                     this.select(0, true);
18066                 }
18067             }else{
18068                 if(this.autoFocus){
18069                     this.selectNext();
18070                 }
18071                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18072                     this.taTask.delay(this.typeAheadDelay);
18073                 }
18074             }
18075         }else{
18076             this.onEmptyResults();
18077         }
18078         
18079         //this.el.focus();
18080     },
18081     // private
18082     onLoadException : function()
18083     {
18084         this.hasQuery = false;
18085         
18086         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18087             this.loading.hide();
18088         }
18089         
18090         if(this.tickable && this.editable){
18091             return;
18092         }
18093         
18094         this.collapse();
18095         // only causes errors at present
18096         //Roo.log(this.store.reader.jsonData);
18097         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18098             // fixme
18099             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18100         //}
18101         
18102         
18103     },
18104     // private
18105     onTypeAhead : function(){
18106         if(this.store.getCount() > 0){
18107             var r = this.store.getAt(0);
18108             var newValue = r.data[this.displayField];
18109             var len = newValue.length;
18110             var selStart = this.getRawValue().length;
18111             
18112             if(selStart != len){
18113                 this.setRawValue(newValue);
18114                 this.selectText(selStart, newValue.length);
18115             }
18116         }
18117     },
18118
18119     // private
18120     onSelect : function(record, index){
18121         
18122         if(this.fireEvent('beforeselect', this, record, index) !== false){
18123         
18124             this.setFromData(index > -1 ? record.data : false);
18125             
18126             this.collapse();
18127             this.fireEvent('select', this, record, index);
18128         }
18129     },
18130
18131     /**
18132      * Returns the currently selected field value or empty string if no value is set.
18133      * @return {String} value The selected value
18134      */
18135     getValue : function()
18136     {
18137         if(Roo.isIOS && this.useNativeIOS){
18138             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18139         }
18140         
18141         if(this.multiple){
18142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18143         }
18144         
18145         if(this.valueField){
18146             return typeof this.value != 'undefined' ? this.value : '';
18147         }else{
18148             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18149         }
18150     },
18151     
18152     getRawValue : function()
18153     {
18154         if(Roo.isIOS && this.useNativeIOS){
18155             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18156         }
18157         
18158         var v = this.inputEl().getValue();
18159         
18160         return v;
18161     },
18162
18163     /**
18164      * Clears any text/value currently set in the field
18165      */
18166     clearValue : function(){
18167         
18168         if(this.hiddenField){
18169             this.hiddenField.dom.value = '';
18170         }
18171         this.value = '';
18172         this.setRawValue('');
18173         this.lastSelectionText = '';
18174         this.lastData = false;
18175         
18176         var close = this.closeTriggerEl();
18177         
18178         if(close){
18179             close.hide();
18180         }
18181         
18182         this.validate();
18183         
18184     },
18185
18186     /**
18187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18188      * will be displayed in the field.  If the value does not match the data value of an existing item,
18189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18190      * Otherwise the field will be blank (although the value will still be set).
18191      * @param {String} value The value to match
18192      */
18193     setValue : function(v)
18194     {
18195         if(Roo.isIOS && this.useNativeIOS){
18196             this.setIOSValue(v);
18197             return;
18198         }
18199         
18200         if(this.multiple){
18201             this.syncValue();
18202             return;
18203         }
18204         
18205         var text = v;
18206         if(this.valueField){
18207             var r = this.findRecord(this.valueField, v);
18208             if(r){
18209                 text = r.data[this.displayField];
18210             }else if(this.valueNotFoundText !== undefined){
18211                 text = this.valueNotFoundText;
18212             }
18213         }
18214         this.lastSelectionText = text;
18215         if(this.hiddenField){
18216             this.hiddenField.dom.value = v;
18217         }
18218         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18219         this.value = v;
18220         
18221         var close = this.closeTriggerEl();
18222         
18223         if(close){
18224             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18225         }
18226         
18227         this.validate();
18228     },
18229     /**
18230      * @property {Object} the last set data for the element
18231      */
18232     
18233     lastData : false,
18234     /**
18235      * Sets the value of the field based on a object which is related to the record format for the store.
18236      * @param {Object} value the value to set as. or false on reset?
18237      */
18238     setFromData : function(o){
18239         
18240         if(this.multiple){
18241             this.addItem(o);
18242             return;
18243         }
18244             
18245         var dv = ''; // display value
18246         var vv = ''; // value value..
18247         this.lastData = o;
18248         if (this.displayField) {
18249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18250         } else {
18251             // this is an error condition!!!
18252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18253         }
18254         
18255         if(this.valueField){
18256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18257         }
18258         
18259         var close = this.closeTriggerEl();
18260         
18261         if(close){
18262             if(dv.length || vv * 1 > 0){
18263                 close.show() ;
18264                 this.blockFocus=true;
18265             } else {
18266                 close.hide();
18267             }             
18268         }
18269         
18270         if(this.hiddenField){
18271             this.hiddenField.dom.value = vv;
18272             
18273             this.lastSelectionText = dv;
18274             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18275             this.value = vv;
18276             return;
18277         }
18278         // no hidden field.. - we store the value in 'value', but still display
18279         // display field!!!!
18280         this.lastSelectionText = dv;
18281         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18282         this.value = vv;
18283         
18284         
18285         
18286     },
18287     // private
18288     reset : function(){
18289         // overridden so that last data is reset..
18290         
18291         if(this.multiple){
18292             this.clearItem();
18293             return;
18294         }
18295         
18296         this.setValue(this.originalValue);
18297         //this.clearInvalid();
18298         this.lastData = false;
18299         if (this.view) {
18300             this.view.clearSelections();
18301         }
18302         
18303         this.validate();
18304     },
18305     // private
18306     findRecord : function(prop, value){
18307         var record;
18308         if(this.store.getCount() > 0){
18309             this.store.each(function(r){
18310                 if(r.data[prop] == value){
18311                     record = r;
18312                     return false;
18313                 }
18314                 return true;
18315             });
18316         }
18317         return record;
18318     },
18319     
18320     getName: function()
18321     {
18322         // returns hidden if it's set..
18323         if (!this.rendered) {return ''};
18324         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18325         
18326     },
18327     // private
18328     onViewMove : function(e, t){
18329         this.inKeyMode = false;
18330     },
18331
18332     // private
18333     onViewOver : function(e, t){
18334         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18335             return;
18336         }
18337         var item = this.view.findItemFromChild(t);
18338         
18339         if(item){
18340             var index = this.view.indexOf(item);
18341             this.select(index, false);
18342         }
18343     },
18344
18345     // private
18346     onViewClick : function(view, doFocus, el, e)
18347     {
18348         var index = this.view.getSelectedIndexes()[0];
18349         
18350         var r = this.store.getAt(index);
18351         
18352         if(this.tickable){
18353             
18354             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18355                 return;
18356             }
18357             
18358             var rm = false;
18359             var _this = this;
18360             
18361             Roo.each(this.tickItems, function(v,k){
18362                 
18363                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18364                     Roo.log(v);
18365                     _this.tickItems.splice(k, 1);
18366                     
18367                     if(typeof(e) == 'undefined' && view == false){
18368                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18369                     }
18370                     
18371                     rm = true;
18372                     return;
18373                 }
18374             });
18375             
18376             if(rm){
18377                 return;
18378             }
18379             
18380             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18381                 this.tickItems.push(r.data);
18382             }
18383             
18384             if(typeof(e) == 'undefined' && view == false){
18385                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18386             }
18387                     
18388             return;
18389         }
18390         
18391         if(r){
18392             this.onSelect(r, index);
18393         }
18394         if(doFocus !== false && !this.blockFocus){
18395             this.inputEl().focus();
18396         }
18397     },
18398
18399     // private
18400     restrictHeight : function(){
18401         //this.innerList.dom.style.height = '';
18402         //var inner = this.innerList.dom;
18403         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18404         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18405         //this.list.beginUpdate();
18406         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18407         this.list.alignTo(this.inputEl(), this.listAlign);
18408         this.list.alignTo(this.inputEl(), this.listAlign);
18409         //this.list.endUpdate();
18410     },
18411
18412     // private
18413     onEmptyResults : function(){
18414         
18415         if(this.tickable && this.editable){
18416             this.hasFocus = false;
18417             this.restrictHeight();
18418             return;
18419         }
18420         
18421         this.collapse();
18422     },
18423
18424     /**
18425      * Returns true if the dropdown list is expanded, else false.
18426      */
18427     isExpanded : function(){
18428         return this.list.isVisible();
18429     },
18430
18431     /**
18432      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18434      * @param {String} value The data value of the item to select
18435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18436      * selected item if it is not currently in view (defaults to true)
18437      * @return {Boolean} True if the value matched an item in the list, else false
18438      */
18439     selectByValue : function(v, scrollIntoView){
18440         if(v !== undefined && v !== null){
18441             var r = this.findRecord(this.valueField || this.displayField, v);
18442             if(r){
18443                 this.select(this.store.indexOf(r), scrollIntoView);
18444                 return true;
18445             }
18446         }
18447         return false;
18448     },
18449
18450     /**
18451      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18453      * @param {Number} index The zero-based index of the list item to select
18454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18455      * selected item if it is not currently in view (defaults to true)
18456      */
18457     select : function(index, scrollIntoView){
18458         this.selectedIndex = index;
18459         this.view.select(index);
18460         if(scrollIntoView !== false){
18461             var el = this.view.getNode(index);
18462             /*
18463              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18464              */
18465             if(el){
18466                 this.list.scrollChildIntoView(el, false);
18467             }
18468         }
18469     },
18470
18471     // private
18472     selectNext : function(){
18473         var ct = this.store.getCount();
18474         if(ct > 0){
18475             if(this.selectedIndex == -1){
18476                 this.select(0);
18477             }else if(this.selectedIndex < ct-1){
18478                 this.select(this.selectedIndex+1);
18479             }
18480         }
18481     },
18482
18483     // private
18484     selectPrev : function(){
18485         var ct = this.store.getCount();
18486         if(ct > 0){
18487             if(this.selectedIndex == -1){
18488                 this.select(0);
18489             }else if(this.selectedIndex != 0){
18490                 this.select(this.selectedIndex-1);
18491             }
18492         }
18493     },
18494
18495     // private
18496     onKeyUp : function(e){
18497         if(this.editable !== false && !e.isSpecialKey()){
18498             this.lastKey = e.getKey();
18499             this.dqTask.delay(this.queryDelay);
18500         }
18501     },
18502
18503     // private
18504     validateBlur : function(){
18505         return !this.list || !this.list.isVisible();   
18506     },
18507
18508     // private
18509     initQuery : function(){
18510         
18511         var v = this.getRawValue();
18512         
18513         if(this.tickable && this.editable){
18514             v = this.tickableInputEl().getValue();
18515         }
18516         
18517         this.doQuery(v);
18518     },
18519
18520     // private
18521     doForce : function(){
18522         if(this.inputEl().dom.value.length > 0){
18523             this.inputEl().dom.value =
18524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18525              
18526         }
18527     },
18528
18529     /**
18530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18531      * query allowing the query action to be canceled if needed.
18532      * @param {String} query The SQL query to execute
18533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18535      * saved in the current store (defaults to false)
18536      */
18537     doQuery : function(q, forceAll){
18538         
18539         if(q === undefined || q === null){
18540             q = '';
18541         }
18542         var qe = {
18543             query: q,
18544             forceAll: forceAll,
18545             combo: this,
18546             cancel:false
18547         };
18548         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18549             return false;
18550         }
18551         q = qe.query;
18552         
18553         forceAll = qe.forceAll;
18554         if(forceAll === true || (q.length >= this.minChars)){
18555             
18556             this.hasQuery = true;
18557             
18558             if(this.lastQuery != q || this.alwaysQuery){
18559                 this.lastQuery = q;
18560                 if(this.mode == 'local'){
18561                     this.selectedIndex = -1;
18562                     if(forceAll){
18563                         this.store.clearFilter();
18564                     }else{
18565                         
18566                         if(this.specialFilter){
18567                             this.fireEvent('specialfilter', this);
18568                             this.onLoad();
18569                             return;
18570                         }
18571                         
18572                         this.store.filter(this.displayField, q);
18573                     }
18574                     
18575                     this.store.fireEvent("datachanged", this.store);
18576                     
18577                     this.onLoad();
18578                     
18579                     
18580                 }else{
18581                     
18582                     this.store.baseParams[this.queryParam] = q;
18583                     
18584                     var options = {params : this.getParams(q)};
18585                     
18586                     if(this.loadNext){
18587                         options.add = true;
18588                         options.params.start = this.page * this.pageSize;
18589                     }
18590                     
18591                     this.store.load(options);
18592                     
18593                     /*
18594                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18595                      *  we should expand the list on onLoad
18596                      *  so command out it
18597                      */
18598 //                    this.expand();
18599                 }
18600             }else{
18601                 this.selectedIndex = -1;
18602                 this.onLoad();   
18603             }
18604         }
18605         
18606         this.loadNext = false;
18607     },
18608     
18609     // private
18610     getParams : function(q){
18611         var p = {};
18612         //p[this.queryParam] = q;
18613         
18614         if(this.pageSize){
18615             p.start = 0;
18616             p.limit = this.pageSize;
18617         }
18618         return p;
18619     },
18620
18621     /**
18622      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18623      */
18624     collapse : function(){
18625         if(!this.isExpanded()){
18626             return;
18627         }
18628         
18629         this.list.hide();
18630         
18631         this.hasFocus = false;
18632         
18633         if(this.tickable){
18634             this.okBtn.hide();
18635             this.cancelBtn.hide();
18636             this.trigger.show();
18637             
18638             if(this.editable){
18639                 this.tickableInputEl().dom.value = '';
18640                 this.tickableInputEl().blur();
18641             }
18642             
18643         }
18644         
18645         Roo.get(document).un('mousedown', this.collapseIf, this);
18646         Roo.get(document).un('mousewheel', this.collapseIf, this);
18647         if (!this.editable) {
18648             Roo.get(document).un('keydown', this.listKeyPress, this);
18649         }
18650         this.fireEvent('collapse', this);
18651         
18652         this.validate();
18653     },
18654
18655     // private
18656     collapseIf : function(e){
18657         var in_combo  = e.within(this.el);
18658         var in_list =  e.within(this.list);
18659         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18660         
18661         if (in_combo || in_list || is_list) {
18662             //e.stopPropagation();
18663             return;
18664         }
18665         
18666         if(this.tickable){
18667             this.onTickableFooterButtonClick(e, false, false);
18668         }
18669
18670         this.collapse();
18671         
18672     },
18673
18674     /**
18675      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18676      */
18677     expand : function(){
18678        
18679         if(this.isExpanded() || !this.hasFocus){
18680             return;
18681         }
18682         
18683         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18684         this.list.setWidth(lw);
18685         
18686         Roo.log('expand');
18687         
18688         this.list.show();
18689         
18690         this.restrictHeight();
18691         
18692         if(this.tickable){
18693             
18694             this.tickItems = Roo.apply([], this.item);
18695             
18696             this.okBtn.show();
18697             this.cancelBtn.show();
18698             this.trigger.hide();
18699             
18700             if(this.editable){
18701                 this.tickableInputEl().focus();
18702             }
18703             
18704         }
18705         
18706         Roo.get(document).on('mousedown', this.collapseIf, this);
18707         Roo.get(document).on('mousewheel', this.collapseIf, this);
18708         if (!this.editable) {
18709             Roo.get(document).on('keydown', this.listKeyPress, this);
18710         }
18711
18712         this.list.setStyle('maxHeight', 'calc(100% - ' + this.list.getTop() + 'px - 50px)');
18713         
18714         this.fireEvent('expand', this);
18715     },
18716
18717     // private
18718     // Implements the default empty TriggerField.onTriggerClick function
18719     onTriggerClick : function(e)
18720     {
18721         Roo.log('trigger click');
18722         
18723         if(this.disabled || !this.triggerList){
18724             return;
18725         }
18726         
18727         this.page = 0;
18728         this.loadNext = false;
18729         
18730         if(this.isExpanded()){
18731             this.collapse();
18732             if (!this.blockFocus) {
18733                 this.inputEl().focus();
18734             }
18735             
18736         }else {
18737             this.hasFocus = true;
18738             if(this.triggerAction == 'all') {
18739                 this.doQuery(this.allQuery, true);
18740             } else {
18741                 this.doQuery(this.getRawValue());
18742             }
18743             if (!this.blockFocus) {
18744                 this.inputEl().focus();
18745             }
18746         }
18747     },
18748     
18749     onTickableTriggerClick : function(e)
18750     {
18751         if(this.disabled){
18752             return;
18753         }
18754         
18755         this.page = 0;
18756         this.loadNext = false;
18757         this.hasFocus = true;
18758         
18759         if(this.triggerAction == 'all') {
18760             this.doQuery(this.allQuery, true);
18761         } else {
18762             this.doQuery(this.getRawValue());
18763         }
18764     },
18765     
18766     onSearchFieldClick : function(e)
18767     {
18768         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18769             this.onTickableFooterButtonClick(e, false, false);
18770             return;
18771         }
18772         
18773         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18774             return;
18775         }
18776         
18777         this.page = 0;
18778         this.loadNext = false;
18779         this.hasFocus = true;
18780         
18781         if(this.triggerAction == 'all') {
18782             this.doQuery(this.allQuery, true);
18783         } else {
18784             this.doQuery(this.getRawValue());
18785         }
18786     },
18787     
18788     listKeyPress : function(e)
18789     {
18790         //Roo.log('listkeypress');
18791         // scroll to first matching element based on key pres..
18792         if (e.isSpecialKey()) {
18793             return false;
18794         }
18795         var k = String.fromCharCode(e.getKey()).toUpperCase();
18796         //Roo.log(k);
18797         var match  = false;
18798         var csel = this.view.getSelectedNodes();
18799         var cselitem = false;
18800         if (csel.length) {
18801             var ix = this.view.indexOf(csel[0]);
18802             cselitem  = this.store.getAt(ix);
18803             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18804                 cselitem = false;
18805             }
18806             
18807         }
18808         
18809         this.store.each(function(v) { 
18810             if (cselitem) {
18811                 // start at existing selection.
18812                 if (cselitem.id == v.id) {
18813                     cselitem = false;
18814                 }
18815                 return true;
18816             }
18817                 
18818             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18819                 match = this.store.indexOf(v);
18820                 return false;
18821             }
18822             return true;
18823         }, this);
18824         
18825         if (match === false) {
18826             return true; // no more action?
18827         }
18828         // scroll to?
18829         this.view.select(match);
18830         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18831         sn.scrollIntoView(sn.dom.parentNode, false);
18832     },
18833     
18834     onViewScroll : function(e, t){
18835         
18836         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){
18837             return;
18838         }
18839         
18840         this.hasQuery = true;
18841         
18842         this.loading = this.list.select('.loading', true).first();
18843         
18844         if(this.loading === null){
18845             this.list.createChild({
18846                 tag: 'div',
18847                 cls: 'loading roo-select2-more-results roo-select2-active',
18848                 html: 'Loading more results...'
18849             });
18850             
18851             this.loading = this.list.select('.loading', true).first();
18852             
18853             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18854             
18855             this.loading.hide();
18856         }
18857         
18858         this.loading.show();
18859         
18860         var _combo = this;
18861         
18862         this.page++;
18863         this.loadNext = true;
18864         
18865         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18866         
18867         return;
18868     },
18869     
18870     addItem : function(o)
18871     {   
18872         var dv = ''; // display value
18873         
18874         if (this.displayField) {
18875             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18876         } else {
18877             // this is an error condition!!!
18878             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18879         }
18880         
18881         if(!dv.length){
18882             return;
18883         }
18884         
18885         var choice = this.choices.createChild({
18886             tag: 'li',
18887             cls: 'roo-select2-search-choice',
18888             cn: [
18889                 {
18890                     tag: 'div',
18891                     html: dv
18892                 },
18893                 {
18894                     tag: 'a',
18895                     href: '#',
18896                     cls: 'roo-select2-search-choice-close fa fa-times',
18897                     tabindex: '-1'
18898                 }
18899             ]
18900             
18901         }, this.searchField);
18902         
18903         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18904         
18905         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18906         
18907         this.item.push(o);
18908         
18909         this.lastData = o;
18910         
18911         this.syncValue();
18912         
18913         this.inputEl().dom.value = '';
18914         
18915         this.validate();
18916     },
18917     
18918     onRemoveItem : function(e, _self, o)
18919     {
18920         e.preventDefault();
18921         
18922         this.lastItem = Roo.apply([], this.item);
18923         
18924         var index = this.item.indexOf(o.data) * 1;
18925         
18926         if( index < 0){
18927             Roo.log('not this item?!');
18928             return;
18929         }
18930         
18931         this.item.splice(index, 1);
18932         o.item.remove();
18933         
18934         this.syncValue();
18935         
18936         this.fireEvent('remove', this, e);
18937         
18938         this.validate();
18939         
18940     },
18941     
18942     syncValue : function()
18943     {
18944         if(!this.item.length){
18945             this.clearValue();
18946             return;
18947         }
18948             
18949         var value = [];
18950         var _this = this;
18951         Roo.each(this.item, function(i){
18952             if(_this.valueField){
18953                 value.push(i[_this.valueField]);
18954                 return;
18955             }
18956
18957             value.push(i);
18958         });
18959
18960         this.value = value.join(',');
18961
18962         if(this.hiddenField){
18963             this.hiddenField.dom.value = this.value;
18964         }
18965         
18966         this.store.fireEvent("datachanged", this.store);
18967         
18968         this.validate();
18969     },
18970     
18971     clearItem : function()
18972     {
18973         if(!this.multiple){
18974             return;
18975         }
18976         
18977         this.item = [];
18978         
18979         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18980            c.remove();
18981         });
18982         
18983         this.syncValue();
18984         
18985         this.validate();
18986         
18987         if(this.tickable && !Roo.isTouch){
18988             this.view.refresh();
18989         }
18990     },
18991     
18992     inputEl: function ()
18993     {
18994         if(Roo.isIOS && this.useNativeIOS){
18995             return this.el.select('select.roo-ios-select', true).first();
18996         }
18997         
18998         if(Roo.isTouch && this.mobileTouchView){
18999             return this.el.select('input.form-control',true).first();
19000         }
19001         
19002         if(this.tickable){
19003             return this.searchField;
19004         }
19005         
19006         return this.el.select('input.form-control',true).first();
19007     },
19008     
19009     onTickableFooterButtonClick : function(e, btn, el)
19010     {
19011         e.preventDefault();
19012         
19013         this.lastItem = Roo.apply([], this.item);
19014         
19015         if(btn && btn.name == 'cancel'){
19016             this.tickItems = Roo.apply([], this.item);
19017             this.collapse();
19018             return;
19019         }
19020         
19021         this.clearItem();
19022         
19023         var _this = this;
19024         
19025         Roo.each(this.tickItems, function(o){
19026             _this.addItem(o);
19027         });
19028         
19029         this.collapse();
19030         
19031     },
19032     
19033     validate : function()
19034     {
19035         if(this.getVisibilityEl().hasClass('hidden')){
19036             return true;
19037         }
19038         
19039         var v = this.getRawValue();
19040         
19041         if(this.multiple){
19042             v = this.getValue();
19043         }
19044         
19045         if(this.disabled || this.allowBlank || v.length){
19046             this.markValid();
19047             return true;
19048         }
19049         
19050         this.markInvalid();
19051         return false;
19052     },
19053     
19054     tickableInputEl : function()
19055     {
19056         if(!this.tickable || !this.editable){
19057             return this.inputEl();
19058         }
19059         
19060         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19061     },
19062     
19063     
19064     getAutoCreateTouchView : function()
19065     {
19066         var id = Roo.id();
19067         
19068         var cfg = {
19069             cls: 'form-group' //input-group
19070         };
19071         
19072         var input =  {
19073             tag: 'input',
19074             id : id,
19075             type : this.inputType,
19076             cls : 'form-control x-combo-noedit',
19077             autocomplete: 'new-password',
19078             placeholder : this.placeholder || '',
19079             readonly : true
19080         };
19081         
19082         if (this.name) {
19083             input.name = this.name;
19084         }
19085         
19086         if (this.size) {
19087             input.cls += ' input-' + this.size;
19088         }
19089         
19090         if (this.disabled) {
19091             input.disabled = true;
19092         }
19093         
19094         var inputblock = {
19095             cls : 'roo-combobox-wrap',
19096             cn : [
19097                 input
19098             ]
19099         };
19100         
19101         if(this.before){
19102             inputblock.cls += ' input-group';
19103             
19104             inputblock.cn.unshift({
19105                 tag :'span',
19106                 cls : 'input-group-addon input-group-prepend input-group-text',
19107                 html : this.before
19108             });
19109         }
19110         
19111         if(this.removable && !this.multiple){
19112             inputblock.cls += ' roo-removable';
19113             
19114             inputblock.cn.push({
19115                 tag: 'button',
19116                 html : 'x',
19117                 cls : 'roo-combo-removable-btn close'
19118             });
19119         }
19120
19121         if(this.hasFeedback && !this.allowBlank){
19122             
19123             inputblock.cls += ' has-feedback';
19124             
19125             inputblock.cn.push({
19126                 tag: 'span',
19127                 cls: 'glyphicon form-control-feedback'
19128             });
19129             
19130         }
19131         
19132         if (this.after) {
19133             
19134             inputblock.cls += (this.before) ? '' : ' input-group';
19135             
19136             inputblock.cn.push({
19137                 tag :'span',
19138                 cls : 'input-group-addon input-group-append input-group-text',
19139                 html : this.after
19140             });
19141         }
19142
19143         
19144         var ibwrap = inputblock;
19145         
19146         if(this.multiple){
19147             ibwrap = {
19148                 tag: 'ul',
19149                 cls: 'roo-select2-choices',
19150                 cn:[
19151                     {
19152                         tag: 'li',
19153                         cls: 'roo-select2-search-field',
19154                         cn: [
19155
19156                             inputblock
19157                         ]
19158                     }
19159                 ]
19160             };
19161         
19162             
19163         }
19164         
19165         var combobox = {
19166             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19167             cn: [
19168                 {
19169                     tag: 'input',
19170                     type : 'hidden',
19171                     cls: 'form-hidden-field'
19172                 },
19173                 ibwrap
19174             ]
19175         };
19176         
19177         if(!this.multiple && this.showToggleBtn){
19178             
19179             var caret = {
19180                 cls: 'caret'
19181             };
19182             
19183             if (this.caret != false) {
19184                 caret = {
19185                      tag: 'i',
19186                      cls: 'fa fa-' + this.caret
19187                 };
19188                 
19189             }
19190             
19191             combobox.cn.push({
19192                 tag :'span',
19193                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19194                 cn : [
19195                     Roo.bootstrap.version == 3 ? caret : '',
19196                     {
19197                         tag: 'span',
19198                         cls: 'combobox-clear',
19199                         cn  : [
19200                             {
19201                                 tag : 'i',
19202                                 cls: 'icon-remove'
19203                             }
19204                         ]
19205                     }
19206                 ]
19207
19208             })
19209         }
19210         
19211         if(this.multiple){
19212             combobox.cls += ' roo-select2-container-multi';
19213         }
19214         
19215         var required =  this.allowBlank ?  {
19216                     tag : 'i',
19217                     style: 'display: none'
19218                 } : {
19219                    tag : 'i',
19220                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19221                    tooltip : 'This field is required'
19222                 };
19223         
19224         var align = this.labelAlign || this.parentLabelAlign();
19225         
19226         if (align ==='left' && this.fieldLabel.length) {
19227
19228             cfg.cn = [
19229                 required,
19230                 {
19231                     tag: 'label',
19232                     cls : 'control-label col-form-label',
19233                     html : this.fieldLabel
19234
19235                 },
19236                 {
19237                     cls : 'roo-combobox-wrap ', 
19238                     cn: [
19239                         combobox
19240                     ]
19241                 }
19242             ];
19243             
19244             var labelCfg = cfg.cn[1];
19245             var contentCfg = cfg.cn[2];
19246             
19247
19248             if(this.indicatorpos == 'right'){
19249                 cfg.cn = [
19250                     {
19251                         tag: 'label',
19252                         'for' :  id,
19253                         cls : 'control-label col-form-label',
19254                         cn : [
19255                             {
19256                                 tag : 'span',
19257                                 html : this.fieldLabel
19258                             },
19259                             required
19260                         ]
19261                     },
19262                     {
19263                         cls : "roo-combobox-wrap ",
19264                         cn: [
19265                             combobox
19266                         ]
19267                     }
19268
19269                 ];
19270                 
19271                 labelCfg = cfg.cn[0];
19272                 contentCfg = cfg.cn[1];
19273             }
19274             
19275            
19276             
19277             if(this.labelWidth > 12){
19278                 labelCfg.style = "width: " + this.labelWidth + 'px';
19279             }
19280            
19281             if(this.labelWidth < 13 && this.labelmd == 0){
19282                 this.labelmd = this.labelWidth;
19283             }
19284             
19285             if(this.labellg > 0){
19286                 labelCfg.cls += ' col-lg-' + this.labellg;
19287                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19288             }
19289             
19290             if(this.labelmd > 0){
19291                 labelCfg.cls += ' col-md-' + this.labelmd;
19292                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19293             }
19294             
19295             if(this.labelsm > 0){
19296                 labelCfg.cls += ' col-sm-' + this.labelsm;
19297                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19298             }
19299             
19300             if(this.labelxs > 0){
19301                 labelCfg.cls += ' col-xs-' + this.labelxs;
19302                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19303             }
19304                 
19305                 
19306         } else if ( this.fieldLabel.length) {
19307             cfg.cn = [
19308                required,
19309                 {
19310                     tag: 'label',
19311                     cls : 'control-label',
19312                     html : this.fieldLabel
19313
19314                 },
19315                 {
19316                     cls : '', 
19317                     cn: [
19318                         combobox
19319                     ]
19320                 }
19321             ];
19322             
19323             if(this.indicatorpos == 'right'){
19324                 cfg.cn = [
19325                     {
19326                         tag: 'label',
19327                         cls : 'control-label',
19328                         html : this.fieldLabel,
19329                         cn : [
19330                             required
19331                         ]
19332                     },
19333                     {
19334                         cls : '', 
19335                         cn: [
19336                             combobox
19337                         ]
19338                     }
19339                 ];
19340             }
19341         } else {
19342             cfg.cn = combobox;    
19343         }
19344         
19345         
19346         var settings = this;
19347         
19348         ['xs','sm','md','lg'].map(function(size){
19349             if (settings[size]) {
19350                 cfg.cls += ' col-' + size + '-' + settings[size];
19351             }
19352         });
19353         
19354         return cfg;
19355     },
19356     
19357     initTouchView : function()
19358     {
19359         this.renderTouchView();
19360         
19361         this.touchViewEl.on('scroll', function(){
19362             this.el.dom.scrollTop = 0;
19363         }, this);
19364         
19365         this.originalValue = this.getValue();
19366         
19367         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19368         
19369         this.inputEl().on("click", this.showTouchView, this);
19370         if (this.triggerEl) {
19371             this.triggerEl.on("click", this.showTouchView, this);
19372         }
19373         
19374         
19375         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19376         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19377         
19378         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19379         
19380         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19381         this.store.on('load', this.onTouchViewLoad, this);
19382         this.store.on('loadexception', this.onTouchViewLoadException, this);
19383         
19384         if(this.hiddenName){
19385             
19386             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19387             
19388             this.hiddenField.dom.value =
19389                 this.hiddenValue !== undefined ? this.hiddenValue :
19390                 this.value !== undefined ? this.value : '';
19391         
19392             this.el.dom.removeAttribute('name');
19393             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19394         }
19395         
19396         if(this.multiple){
19397             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19398             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19399         }
19400         
19401         if(this.removable && !this.multiple){
19402             var close = this.closeTriggerEl();
19403             if(close){
19404                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19405                 close.on('click', this.removeBtnClick, this, close);
19406             }
19407         }
19408         /*
19409          * fix the bug in Safari iOS8
19410          */
19411         this.inputEl().on("focus", function(e){
19412             document.activeElement.blur();
19413         }, this);
19414         
19415         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19416         
19417         return;
19418         
19419         
19420     },
19421     
19422     renderTouchView : function()
19423     {
19424         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19425         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19426         
19427         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19428         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19429         
19430         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19431         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19432         this.touchViewBodyEl.setStyle('overflow', 'auto');
19433         
19434         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19435         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19436         
19437         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19438         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19439         
19440     },
19441     
19442     showTouchView : function()
19443     {
19444         if(this.disabled){
19445             return;
19446         }
19447         
19448         this.touchViewHeaderEl.hide();
19449
19450         if(this.modalTitle.length){
19451             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19452             this.touchViewHeaderEl.show();
19453         }
19454
19455         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19456         this.touchViewEl.show();
19457
19458         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19459         
19460         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19461         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19462
19463         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19464
19465         if(this.modalTitle.length){
19466             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19467         }
19468         
19469         this.touchViewBodyEl.setHeight(bodyHeight);
19470
19471         if(this.animate){
19472             var _this = this;
19473             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19474         }else{
19475             this.touchViewEl.addClass(['in','show']);
19476         }
19477         
19478         if(this._touchViewMask){
19479             Roo.get(document.body).addClass("x-body-masked");
19480             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19481             this._touchViewMask.setStyle('z-index', 10000);
19482             this._touchViewMask.addClass('show');
19483         }
19484         
19485         this.doTouchViewQuery();
19486         
19487     },
19488     
19489     hideTouchView : function()
19490     {
19491         this.touchViewEl.removeClass(['in','show']);
19492
19493         if(this.animate){
19494             var _this = this;
19495             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19496         }else{
19497             this.touchViewEl.setStyle('display', 'none');
19498         }
19499         
19500         if(this._touchViewMask){
19501             this._touchViewMask.removeClass('show');
19502             Roo.get(document.body).removeClass("x-body-masked");
19503         }
19504     },
19505     
19506     setTouchViewValue : function()
19507     {
19508         if(this.multiple){
19509             this.clearItem();
19510         
19511             var _this = this;
19512
19513             Roo.each(this.tickItems, function(o){
19514                 this.addItem(o);
19515             }, this);
19516         }
19517         
19518         this.hideTouchView();
19519     },
19520     
19521     doTouchViewQuery : function()
19522     {
19523         var qe = {
19524             query: '',
19525             forceAll: true,
19526             combo: this,
19527             cancel:false
19528         };
19529         
19530         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19531             return false;
19532         }
19533         
19534         if(!this.alwaysQuery || this.mode == 'local'){
19535             this.onTouchViewLoad();
19536             return;
19537         }
19538         
19539         this.store.load();
19540     },
19541     
19542     onTouchViewBeforeLoad : function(combo,opts)
19543     {
19544         return;
19545     },
19546
19547     // private
19548     onTouchViewLoad : function()
19549     {
19550         if(this.store.getCount() < 1){
19551             this.onTouchViewEmptyResults();
19552             return;
19553         }
19554         
19555         this.clearTouchView();
19556         
19557         var rawValue = this.getRawValue();
19558         
19559         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19560         
19561         this.tickItems = [];
19562         
19563         this.store.data.each(function(d, rowIndex){
19564             var row = this.touchViewListGroup.createChild(template);
19565             
19566             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19567                 row.addClass(d.data.cls);
19568             }
19569             
19570             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19571                 var cfg = {
19572                     data : d.data,
19573                     html : d.data[this.displayField]
19574                 };
19575                 
19576                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19577                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19578                 }
19579             }
19580             row.removeClass('selected');
19581             if(!this.multiple && this.valueField &&
19582                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19583             {
19584                 // radio buttons..
19585                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19586                 row.addClass('selected');
19587             }
19588             
19589             if(this.multiple && this.valueField &&
19590                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19591             {
19592                 
19593                 // checkboxes...
19594                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19595                 this.tickItems.push(d.data);
19596             }
19597             
19598             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19599             
19600         }, this);
19601         
19602         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19603         
19604         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19605
19606         if(this.modalTitle.length){
19607             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19608         }
19609
19610         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19611         
19612         if(this.mobile_restrict_height && listHeight < bodyHeight){
19613             this.touchViewBodyEl.setHeight(listHeight);
19614         }
19615         
19616         var _this = this;
19617         
19618         if(firstChecked && listHeight > bodyHeight){
19619             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19620         }
19621         
19622     },
19623     
19624     onTouchViewLoadException : function()
19625     {
19626         this.hideTouchView();
19627     },
19628     
19629     onTouchViewEmptyResults : function()
19630     {
19631         this.clearTouchView();
19632         
19633         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19634         
19635         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19636         
19637     },
19638     
19639     clearTouchView : function()
19640     {
19641         this.touchViewListGroup.dom.innerHTML = '';
19642     },
19643     
19644     onTouchViewClick : function(e, el, o)
19645     {
19646         e.preventDefault();
19647         
19648         var row = o.row;
19649         var rowIndex = o.rowIndex;
19650         
19651         var r = this.store.getAt(rowIndex);
19652         
19653         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19654             
19655             if(!this.multiple){
19656                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19657                     c.dom.removeAttribute('checked');
19658                 }, this);
19659
19660                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19661
19662                 this.setFromData(r.data);
19663
19664                 var close = this.closeTriggerEl();
19665
19666                 if(close){
19667                     close.show();
19668                 }
19669
19670                 this.hideTouchView();
19671
19672                 this.fireEvent('select', this, r, rowIndex);
19673
19674                 return;
19675             }
19676
19677             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19678                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19679                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19680                 return;
19681             }
19682
19683             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19684             this.addItem(r.data);
19685             this.tickItems.push(r.data);
19686         }
19687     },
19688     
19689     getAutoCreateNativeIOS : function()
19690     {
19691         var cfg = {
19692             cls: 'form-group' //input-group,
19693         };
19694         
19695         var combobox =  {
19696             tag: 'select',
19697             cls : 'roo-ios-select'
19698         };
19699         
19700         if (this.name) {
19701             combobox.name = this.name;
19702         }
19703         
19704         if (this.disabled) {
19705             combobox.disabled = true;
19706         }
19707         
19708         var settings = this;
19709         
19710         ['xs','sm','md','lg'].map(function(size){
19711             if (settings[size]) {
19712                 cfg.cls += ' col-' + size + '-' + settings[size];
19713             }
19714         });
19715         
19716         cfg.cn = combobox;
19717         
19718         return cfg;
19719         
19720     },
19721     
19722     initIOSView : function()
19723     {
19724         this.store.on('load', this.onIOSViewLoad, this);
19725         
19726         return;
19727     },
19728     
19729     onIOSViewLoad : function()
19730     {
19731         if(this.store.getCount() < 1){
19732             return;
19733         }
19734         
19735         this.clearIOSView();
19736         
19737         if(this.allowBlank) {
19738             
19739             var default_text = '-- SELECT --';
19740             
19741             if(this.placeholder.length){
19742                 default_text = this.placeholder;
19743             }
19744             
19745             if(this.emptyTitle.length){
19746                 default_text += ' - ' + this.emptyTitle + ' -';
19747             }
19748             
19749             var opt = this.inputEl().createChild({
19750                 tag: 'option',
19751                 value : 0,
19752                 html : default_text
19753             });
19754             
19755             var o = {};
19756             o[this.valueField] = 0;
19757             o[this.displayField] = default_text;
19758             
19759             this.ios_options.push({
19760                 data : o,
19761                 el : opt
19762             });
19763             
19764         }
19765         
19766         this.store.data.each(function(d, rowIndex){
19767             
19768             var html = '';
19769             
19770             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19771                 html = d.data[this.displayField];
19772             }
19773             
19774             var value = '';
19775             
19776             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19777                 value = d.data[this.valueField];
19778             }
19779             
19780             var option = {
19781                 tag: 'option',
19782                 value : value,
19783                 html : html
19784             };
19785             
19786             if(this.value == d.data[this.valueField]){
19787                 option['selected'] = true;
19788             }
19789             
19790             var opt = this.inputEl().createChild(option);
19791             
19792             this.ios_options.push({
19793                 data : d.data,
19794                 el : opt
19795             });
19796             
19797         }, this);
19798         
19799         this.inputEl().on('change', function(){
19800            this.fireEvent('select', this);
19801         }, this);
19802         
19803     },
19804     
19805     clearIOSView: function()
19806     {
19807         this.inputEl().dom.innerHTML = '';
19808         
19809         this.ios_options = [];
19810     },
19811     
19812     setIOSValue: function(v)
19813     {
19814         this.value = v;
19815         
19816         if(!this.ios_options){
19817             return;
19818         }
19819         
19820         Roo.each(this.ios_options, function(opts){
19821            
19822            opts.el.dom.removeAttribute('selected');
19823            
19824            if(opts.data[this.valueField] != v){
19825                return;
19826            }
19827            
19828            opts.el.dom.setAttribute('selected', true);
19829            
19830         }, this);
19831     }
19832
19833     /** 
19834     * @cfg {Boolean} grow 
19835     * @hide 
19836     */
19837     /** 
19838     * @cfg {Number} growMin 
19839     * @hide 
19840     */
19841     /** 
19842     * @cfg {Number} growMax 
19843     * @hide 
19844     */
19845     /**
19846      * @hide
19847      * @method autoSize
19848      */
19849 });
19850
19851 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19852     
19853     header : {
19854         tag: 'div',
19855         cls: 'modal-header',
19856         cn: [
19857             {
19858                 tag: 'h4',
19859                 cls: 'modal-title'
19860             }
19861         ]
19862     },
19863     
19864     body : {
19865         tag: 'div',
19866         cls: 'modal-body',
19867         cn: [
19868             {
19869                 tag: 'ul',
19870                 cls: 'list-group'
19871             }
19872         ]
19873     },
19874     
19875     listItemRadio : {
19876         tag: 'li',
19877         cls: 'list-group-item',
19878         cn: [
19879             {
19880                 tag: 'span',
19881                 cls: 'roo-combobox-list-group-item-value'
19882             },
19883             {
19884                 tag: 'div',
19885                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19886                 cn: [
19887                     {
19888                         tag: 'input',
19889                         type: 'radio'
19890                     },
19891                     {
19892                         tag: 'label'
19893                     }
19894                 ]
19895             }
19896         ]
19897     },
19898     
19899     listItemCheckbox : {
19900         tag: 'li',
19901         cls: 'list-group-item',
19902         cn: [
19903             {
19904                 tag: 'span',
19905                 cls: 'roo-combobox-list-group-item-value'
19906             },
19907             {
19908                 tag: 'div',
19909                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19910                 cn: [
19911                     {
19912                         tag: 'input',
19913                         type: 'checkbox'
19914                     },
19915                     {
19916                         tag: 'label'
19917                     }
19918                 ]
19919             }
19920         ]
19921     },
19922     
19923     emptyResult : {
19924         tag: 'div',
19925         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19926     },
19927     
19928     footer : {
19929         tag: 'div',
19930         cls: 'modal-footer',
19931         cn: [
19932             {
19933                 tag: 'div',
19934                 cls: 'row',
19935                 cn: [
19936                     {
19937                         tag: 'div',
19938                         cls: 'col-xs-6 text-left',
19939                         cn: {
19940                             tag: 'button',
19941                             cls: 'btn btn-danger roo-touch-view-cancel',
19942                             html: 'Cancel'
19943                         }
19944                     },
19945                     {
19946                         tag: 'div',
19947                         cls: 'col-xs-6 text-right',
19948                         cn: {
19949                             tag: 'button',
19950                             cls: 'btn btn-success roo-touch-view-ok',
19951                             html: 'OK'
19952                         }
19953                     }
19954                 ]
19955             }
19956         ]
19957         
19958     }
19959 });
19960
19961 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19962     
19963     touchViewTemplate : {
19964         tag: 'div',
19965         cls: 'modal fade roo-combobox-touch-view',
19966         cn: [
19967             {
19968                 tag: 'div',
19969                 cls: 'modal-dialog',
19970                 style : 'position:fixed', // we have to fix position....
19971                 cn: [
19972                     {
19973                         tag: 'div',
19974                         cls: 'modal-content',
19975                         cn: [
19976                             Roo.bootstrap.form.ComboBox.header,
19977                             Roo.bootstrap.form.ComboBox.body,
19978                             Roo.bootstrap.form.ComboBox.footer
19979                         ]
19980                     }
19981                 ]
19982             }
19983         ]
19984     }
19985 });/*
19986  * Based on:
19987  * Ext JS Library 1.1.1
19988  * Copyright(c) 2006-2007, Ext JS, LLC.
19989  *
19990  * Originally Released Under LGPL - original licence link has changed is not relivant.
19991  *
19992  * Fork - LGPL
19993  * <script type="text/javascript">
19994  */
19995
19996 /**
19997  * @class Roo.View
19998  * @extends Roo.util.Observable
19999  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
20000  * This class also supports single and multi selection modes. <br>
20001  * Create a data model bound view:
20002  <pre><code>
20003  var store = new Roo.data.Store(...);
20004
20005  var view = new Roo.View({
20006     el : "my-element",
20007     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
20008  
20009     singleSelect: true,
20010     selectedClass: "ydataview-selected",
20011     store: store
20012  });
20013
20014  // listen for node click?
20015  view.on("click", function(vw, index, node, e){
20016  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
20017  });
20018
20019  // load XML data
20020  dataModel.load("foobar.xml");
20021  </code></pre>
20022  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
20023  * <br><br>
20024  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
20025  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
20026  * 
20027  * Note: old style constructor is still suported (container, template, config)
20028  * 
20029  * @constructor
20030  * Create a new View
20031  * @param {Object} config The config object
20032  * 
20033  */
20034 Roo.View = function(config, depreciated_tpl, depreciated_config){
20035     
20036     this.parent = false;
20037     
20038     if (typeof(depreciated_tpl) == 'undefined') {
20039         // new way.. - universal constructor.
20040         Roo.apply(this, config);
20041         this.el  = Roo.get(this.el);
20042     } else {
20043         // old format..
20044         this.el  = Roo.get(config);
20045         this.tpl = depreciated_tpl;
20046         Roo.apply(this, depreciated_config);
20047     }
20048     this.wrapEl  = this.el.wrap().wrap();
20049     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20050     
20051     
20052     if(typeof(this.tpl) == "string"){
20053         this.tpl = new Roo.Template(this.tpl);
20054     } else {
20055         // support xtype ctors..
20056         this.tpl = new Roo.factory(this.tpl, Roo);
20057     }
20058     
20059     
20060     this.tpl.compile();
20061     
20062     /** @private */
20063     this.addEvents({
20064         /**
20065          * @event beforeclick
20066          * Fires before a click is processed. Returns false to cancel the default action.
20067          * @param {Roo.View} this
20068          * @param {Number} index The index of the target node
20069          * @param {HTMLElement} node The target node
20070          * @param {Roo.EventObject} e The raw event object
20071          */
20072             "beforeclick" : true,
20073         /**
20074          * @event click
20075          * Fires when a template node is clicked.
20076          * @param {Roo.View} this
20077          * @param {Number} index The index of the target node
20078          * @param {HTMLElement} node The target node
20079          * @param {Roo.EventObject} e The raw event object
20080          */
20081             "click" : true,
20082         /**
20083          * @event dblclick
20084          * Fires when a template node is double clicked.
20085          * @param {Roo.View} this
20086          * @param {Number} index The index of the target node
20087          * @param {HTMLElement} node The target node
20088          * @param {Roo.EventObject} e The raw event object
20089          */
20090             "dblclick" : true,
20091         /**
20092          * @event contextmenu
20093          * Fires when a template node is right clicked.
20094          * @param {Roo.View} this
20095          * @param {Number} index The index of the target node
20096          * @param {HTMLElement} node The target node
20097          * @param {Roo.EventObject} e The raw event object
20098          */
20099             "contextmenu" : true,
20100         /**
20101          * @event selectionchange
20102          * Fires when the selected nodes change.
20103          * @param {Roo.View} this
20104          * @param {Array} selections Array of the selected nodes
20105          */
20106             "selectionchange" : true,
20107     
20108         /**
20109          * @event beforeselect
20110          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20111          * @param {Roo.View} this
20112          * @param {HTMLElement} node The node to be selected
20113          * @param {Array} selections Array of currently selected nodes
20114          */
20115             "beforeselect" : true,
20116         /**
20117          * @event preparedata
20118          * Fires on every row to render, to allow you to change the data.
20119          * @param {Roo.View} this
20120          * @param {Object} data to be rendered (change this)
20121          */
20122           "preparedata" : true
20123           
20124           
20125         });
20126
20127
20128
20129     this.el.on({
20130         "click": this.onClick,
20131         "dblclick": this.onDblClick,
20132         "contextmenu": this.onContextMenu,
20133         scope:this
20134     });
20135
20136     this.selections = [];
20137     this.nodes = [];
20138     this.cmp = new Roo.CompositeElementLite([]);
20139     if(this.store){
20140         this.store = Roo.factory(this.store, Roo.data);
20141         this.setStore(this.store, true);
20142     }
20143     
20144     if ( this.footer && this.footer.xtype) {
20145            
20146          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20147         
20148         this.footer.dataSource = this.store;
20149         this.footer.container = fctr;
20150         this.footer = Roo.factory(this.footer, Roo);
20151         fctr.insertFirst(this.el);
20152         
20153         // this is a bit insane - as the paging toolbar seems to detach the el..
20154 //        dom.parentNode.parentNode.parentNode
20155          // they get detached?
20156     }
20157     
20158     
20159     Roo.View.superclass.constructor.call(this);
20160     
20161     
20162 };
20163
20164 Roo.extend(Roo.View, Roo.util.Observable, {
20165     
20166      /**
20167      * @cfg {Roo.data.Store} store Data store to load data from.
20168      */
20169     store : false,
20170     
20171     /**
20172      * @cfg {String|Roo.Element} el The container element.
20173      */
20174     el : '',
20175     
20176     /**
20177      * @cfg {String|Roo.Template} tpl The template used by this View 
20178      */
20179     tpl : false,
20180     /**
20181      * @cfg {String} dataName the named area of the template to use as the data area
20182      *                          Works with domtemplates roo-name="name"
20183      */
20184     dataName: false,
20185     /**
20186      * @cfg {String} selectedClass The css class to add to selected nodes
20187      */
20188     selectedClass : "x-view-selected",
20189      /**
20190      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20191      */
20192     emptyText : "",
20193     
20194     /**
20195      * @cfg {String} text to display on mask (default Loading)
20196      */
20197     mask : false,
20198     /**
20199      * @cfg {Boolean} multiSelect Allow multiple selection
20200      */
20201     multiSelect : false,
20202     /**
20203      * @cfg {Boolean} singleSelect Allow single selection
20204      */
20205     singleSelect:  false,
20206     
20207     /**
20208      * @cfg {Boolean} toggleSelect - selecting 
20209      */
20210     toggleSelect : false,
20211     
20212     /**
20213      * @cfg {Boolean} tickable - selecting 
20214      */
20215     tickable : false,
20216     
20217     /**
20218      * Returns the element this view is bound to.
20219      * @return {Roo.Element}
20220      */
20221     getEl : function(){
20222         return this.wrapEl;
20223     },
20224     
20225     
20226
20227     /**
20228      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20229      */
20230     refresh : function(){
20231         //Roo.log('refresh');
20232         var t = this.tpl;
20233         
20234         // if we are using something like 'domtemplate', then
20235         // the what gets used is:
20236         // t.applySubtemplate(NAME, data, wrapping data..)
20237         // the outer template then get' applied with
20238         //     the store 'extra data'
20239         // and the body get's added to the
20240         //      roo-name="data" node?
20241         //      <span class='roo-tpl-{name}'></span> ?????
20242         
20243         
20244         
20245         this.clearSelections();
20246         this.el.update("");
20247         var html = [];
20248         var records = this.store.getRange();
20249         if(records.length < 1) {
20250             
20251             // is this valid??  = should it render a template??
20252             
20253             this.el.update(this.emptyText);
20254             return;
20255         }
20256         var el = this.el;
20257         if (this.dataName) {
20258             this.el.update(t.apply(this.store.meta)); //????
20259             el = this.el.child('.roo-tpl-' + this.dataName);
20260         }
20261         
20262         for(var i = 0, len = records.length; i < len; i++){
20263             var data = this.prepareData(records[i].data, i, records[i]);
20264             this.fireEvent("preparedata", this, data, i, records[i]);
20265             
20266             var d = Roo.apply({}, data);
20267             
20268             if(this.tickable){
20269                 Roo.apply(d, {'roo-id' : Roo.id()});
20270                 
20271                 var _this = this;
20272             
20273                 Roo.each(this.parent.item, function(item){
20274                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20275                         return;
20276                     }
20277                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20278                 });
20279             }
20280             
20281             html[html.length] = Roo.util.Format.trim(
20282                 this.dataName ?
20283                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20284                     t.apply(d)
20285             );
20286         }
20287         
20288         
20289         
20290         el.update(html.join(""));
20291         this.nodes = el.dom.childNodes;
20292         this.updateIndexes(0);
20293     },
20294     
20295
20296     /**
20297      * Function to override to reformat the data that is sent to
20298      * the template for each node.
20299      * DEPRICATED - use the preparedata event handler.
20300      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20301      * a JSON object for an UpdateManager bound view).
20302      */
20303     prepareData : function(data, index, record)
20304     {
20305         this.fireEvent("preparedata", this, data, index, record);
20306         return data;
20307     },
20308
20309     onUpdate : function(ds, record){
20310         // Roo.log('on update');   
20311         this.clearSelections();
20312         var index = this.store.indexOf(record);
20313         var n = this.nodes[index];
20314         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20315         n.parentNode.removeChild(n);
20316         this.updateIndexes(index, index);
20317     },
20318
20319     
20320     
20321 // --------- FIXME     
20322     onAdd : function(ds, records, index)
20323     {
20324         //Roo.log(['on Add', ds, records, index] );        
20325         this.clearSelections();
20326         if(this.nodes.length == 0){
20327             this.refresh();
20328             return;
20329         }
20330         var n = this.nodes[index];
20331         for(var i = 0, len = records.length; i < len; i++){
20332             var d = this.prepareData(records[i].data, i, records[i]);
20333             if(n){
20334                 this.tpl.insertBefore(n, d);
20335             }else{
20336                 
20337                 this.tpl.append(this.el, d);
20338             }
20339         }
20340         this.updateIndexes(index);
20341     },
20342
20343     onRemove : function(ds, record, index){
20344        // Roo.log('onRemove');
20345         this.clearSelections();
20346         var el = this.dataName  ?
20347             this.el.child('.roo-tpl-' + this.dataName) :
20348             this.el; 
20349         
20350         el.dom.removeChild(this.nodes[index]);
20351         this.updateIndexes(index);
20352     },
20353
20354     /**
20355      * Refresh an individual node.
20356      * @param {Number} index
20357      */
20358     refreshNode : function(index){
20359         this.onUpdate(this.store, this.store.getAt(index));
20360     },
20361
20362     updateIndexes : function(startIndex, endIndex){
20363         var ns = this.nodes;
20364         startIndex = startIndex || 0;
20365         endIndex = endIndex || ns.length - 1;
20366         for(var i = startIndex; i <= endIndex; i++){
20367             ns[i].nodeIndex = i;
20368         }
20369     },
20370
20371     /**
20372      * Changes the data store this view uses and refresh the view.
20373      * @param {Store} store
20374      */
20375     setStore : function(store, initial){
20376         if(!initial && this.store){
20377             this.store.un("datachanged", this.refresh);
20378             this.store.un("add", this.onAdd);
20379             this.store.un("remove", this.onRemove);
20380             this.store.un("update", this.onUpdate);
20381             this.store.un("clear", this.refresh);
20382             this.store.un("beforeload", this.onBeforeLoad);
20383             this.store.un("load", this.onLoad);
20384             this.store.un("loadexception", this.onLoad);
20385         }
20386         if(store){
20387           
20388             store.on("datachanged", this.refresh, this);
20389             store.on("add", this.onAdd, this);
20390             store.on("remove", this.onRemove, this);
20391             store.on("update", this.onUpdate, this);
20392             store.on("clear", this.refresh, this);
20393             store.on("beforeload", this.onBeforeLoad, this);
20394             store.on("load", this.onLoad, this);
20395             store.on("loadexception", this.onLoad, this);
20396         }
20397         
20398         if(store){
20399             this.refresh();
20400         }
20401     },
20402     /**
20403      * onbeforeLoad - masks the loading area.
20404      *
20405      */
20406     onBeforeLoad : function(store,opts)
20407     {
20408          //Roo.log('onBeforeLoad');   
20409         if (!opts.add) {
20410             this.el.update("");
20411         }
20412         this.el.mask(this.mask ? this.mask : "Loading" ); 
20413     },
20414     onLoad : function ()
20415     {
20416         this.el.unmask();
20417     },
20418     
20419
20420     /**
20421      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20422      * @param {HTMLElement} node
20423      * @return {HTMLElement} The template node
20424      */
20425     findItemFromChild : function(node){
20426         var el = this.dataName  ?
20427             this.el.child('.roo-tpl-' + this.dataName,true) :
20428             this.el.dom; 
20429         
20430         if(!node || node.parentNode == el){
20431                     return node;
20432             }
20433             var p = node.parentNode;
20434             while(p && p != el){
20435             if(p.parentNode == el){
20436                 return p;
20437             }
20438             p = p.parentNode;
20439         }
20440             return null;
20441     },
20442
20443     /** @ignore */
20444     onClick : function(e){
20445         var item = this.findItemFromChild(e.getTarget());
20446         if(item){
20447             var index = this.indexOf(item);
20448             if(this.onItemClick(item, index, e) !== false){
20449                 this.fireEvent("click", this, index, item, e);
20450             }
20451         }else{
20452             this.clearSelections();
20453         }
20454     },
20455
20456     /** @ignore */
20457     onContextMenu : function(e){
20458         var item = this.findItemFromChild(e.getTarget());
20459         if(item){
20460             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20461         }
20462     },
20463
20464     /** @ignore */
20465     onDblClick : function(e){
20466         var item = this.findItemFromChild(e.getTarget());
20467         if(item){
20468             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20469         }
20470     },
20471
20472     onItemClick : function(item, index, e)
20473     {
20474         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20475             return false;
20476         }
20477         if (this.toggleSelect) {
20478             var m = this.isSelected(item) ? 'unselect' : 'select';
20479             //Roo.log(m);
20480             var _t = this;
20481             _t[m](item, true, false);
20482             return true;
20483         }
20484         if(this.multiSelect || this.singleSelect){
20485             if(this.multiSelect && e.shiftKey && this.lastSelection){
20486                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20487             }else{
20488                 this.select(item, this.multiSelect && e.ctrlKey);
20489                 this.lastSelection = item;
20490             }
20491             
20492             if(!this.tickable){
20493                 e.preventDefault();
20494             }
20495             
20496         }
20497         return true;
20498     },
20499
20500     /**
20501      * Get the number of selected nodes.
20502      * @return {Number}
20503      */
20504     getSelectionCount : function(){
20505         return this.selections.length;
20506     },
20507
20508     /**
20509      * Get the currently selected nodes.
20510      * @return {Array} An array of HTMLElements
20511      */
20512     getSelectedNodes : function(){
20513         return this.selections;
20514     },
20515
20516     /**
20517      * Get the indexes of the selected nodes.
20518      * @return {Array}
20519      */
20520     getSelectedIndexes : function(){
20521         var indexes = [], s = this.selections;
20522         for(var i = 0, len = s.length; i < len; i++){
20523             indexes.push(s[i].nodeIndex);
20524         }
20525         return indexes;
20526     },
20527
20528     /**
20529      * Clear all selections
20530      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20531      */
20532     clearSelections : function(suppressEvent){
20533         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20534             this.cmp.elements = this.selections;
20535             this.cmp.removeClass(this.selectedClass);
20536             this.selections = [];
20537             if(!suppressEvent){
20538                 this.fireEvent("selectionchange", this, this.selections);
20539             }
20540         }
20541     },
20542
20543     /**
20544      * Returns true if the passed node is selected
20545      * @param {HTMLElement/Number} node The node or node index
20546      * @return {Boolean}
20547      */
20548     isSelected : function(node){
20549         var s = this.selections;
20550         if(s.length < 1){
20551             return false;
20552         }
20553         node = this.getNode(node);
20554         return s.indexOf(node) !== -1;
20555     },
20556
20557     /**
20558      * Selects nodes.
20559      * @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
20560      * @param {Boolean} keepExisting (optional) true to keep existing selections
20561      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20562      */
20563     select : function(nodeInfo, keepExisting, suppressEvent){
20564         if(nodeInfo instanceof Array){
20565             if(!keepExisting){
20566                 this.clearSelections(true);
20567             }
20568             for(var i = 0, len = nodeInfo.length; i < len; i++){
20569                 this.select(nodeInfo[i], true, true);
20570             }
20571             return;
20572         } 
20573         var node = this.getNode(nodeInfo);
20574         if(!node || this.isSelected(node)){
20575             return; // already selected.
20576         }
20577         if(!keepExisting){
20578             this.clearSelections(true);
20579         }
20580         
20581         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20582             Roo.fly(node).addClass(this.selectedClass);
20583             this.selections.push(node);
20584             if(!suppressEvent){
20585                 this.fireEvent("selectionchange", this, this.selections);
20586             }
20587         }
20588         
20589         
20590     },
20591       /**
20592      * Unselects nodes.
20593      * @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
20594      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20595      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20596      */
20597     unselect : function(nodeInfo, keepExisting, suppressEvent)
20598     {
20599         if(nodeInfo instanceof Array){
20600             Roo.each(this.selections, function(s) {
20601                 this.unselect(s, nodeInfo);
20602             }, this);
20603             return;
20604         }
20605         var node = this.getNode(nodeInfo);
20606         if(!node || !this.isSelected(node)){
20607             //Roo.log("not selected");
20608             return; // not selected.
20609         }
20610         // fireevent???
20611         var ns = [];
20612         Roo.each(this.selections, function(s) {
20613             if (s == node ) {
20614                 Roo.fly(node).removeClass(this.selectedClass);
20615
20616                 return;
20617             }
20618             ns.push(s);
20619         },this);
20620         
20621         this.selections= ns;
20622         this.fireEvent("selectionchange", this, this.selections);
20623     },
20624
20625     /**
20626      * Gets a template node.
20627      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20628      * @return {HTMLElement} The node or null if it wasn't found
20629      */
20630     getNode : function(nodeInfo){
20631         if(typeof nodeInfo == "string"){
20632             return document.getElementById(nodeInfo);
20633         }else if(typeof nodeInfo == "number"){
20634             return this.nodes[nodeInfo];
20635         }
20636         return nodeInfo;
20637     },
20638
20639     /**
20640      * Gets a range template nodes.
20641      * @param {Number} startIndex
20642      * @param {Number} endIndex
20643      * @return {Array} An array of nodes
20644      */
20645     getNodes : function(start, end){
20646         var ns = this.nodes;
20647         start = start || 0;
20648         end = typeof end == "undefined" ? ns.length - 1 : end;
20649         var nodes = [];
20650         if(start <= end){
20651             for(var i = start; i <= end; i++){
20652                 nodes.push(ns[i]);
20653             }
20654         } else{
20655             for(var i = start; i >= end; i--){
20656                 nodes.push(ns[i]);
20657             }
20658         }
20659         return nodes;
20660     },
20661
20662     /**
20663      * Finds the index of the passed node
20664      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20665      * @return {Number} The index of the node or -1
20666      */
20667     indexOf : function(node){
20668         node = this.getNode(node);
20669         if(typeof node.nodeIndex == "number"){
20670             return node.nodeIndex;
20671         }
20672         var ns = this.nodes;
20673         for(var i = 0, len = ns.length; i < len; i++){
20674             if(ns[i] == node){
20675                 return i;
20676             }
20677         }
20678         return -1;
20679     }
20680 });
20681 /*
20682  * - LGPL
20683  *
20684  * based on jquery fullcalendar
20685  * 
20686  */
20687
20688 Roo.bootstrap = Roo.bootstrap || {};
20689 /**
20690  * @class Roo.bootstrap.Calendar
20691  * @extends Roo.bootstrap.Component
20692  * Bootstrap Calendar class
20693  * @cfg {Boolean} loadMask (true|false) default false
20694  * @cfg {Object} header generate the user specific header of the calendar, default false
20695
20696  * @constructor
20697  * Create a new Container
20698  * @param {Object} config The config object
20699  */
20700
20701
20702
20703 Roo.bootstrap.Calendar = function(config){
20704     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20705      this.addEvents({
20706         /**
20707              * @event select
20708              * Fires when a date is selected
20709              * @param {DatePicker} this
20710              * @param {Date} date The selected date
20711              */
20712         'select': true,
20713         /**
20714              * @event monthchange
20715              * Fires when the displayed month changes 
20716              * @param {DatePicker} this
20717              * @param {Date} date The selected month
20718              */
20719         'monthchange': true,
20720         /**
20721              * @event evententer
20722              * Fires when mouse over an event
20723              * @param {Calendar} this
20724              * @param {event} Event
20725              */
20726         'evententer': true,
20727         /**
20728              * @event eventleave
20729              * Fires when the mouse leaves an
20730              * @param {Calendar} this
20731              * @param {event}
20732              */
20733         'eventleave': true,
20734         /**
20735              * @event eventclick
20736              * Fires when the mouse click an
20737              * @param {Calendar} this
20738              * @param {event}
20739              */
20740         'eventclick': true
20741         
20742     });
20743
20744 };
20745
20746 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20747     
20748           /**
20749      * @cfg {Roo.data.Store} store
20750      * The data source for the calendar
20751      */
20752         store : false,
20753      /**
20754      * @cfg {Number} startDay
20755      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20756      */
20757     startDay : 0,
20758     
20759     loadMask : false,
20760     
20761     header : false,
20762       
20763     getAutoCreate : function(){
20764         
20765         
20766         var fc_button = function(name, corner, style, content ) {
20767             return Roo.apply({},{
20768                 tag : 'span',
20769                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20770                          (corner.length ?
20771                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20772                             ''
20773                         ),
20774                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20775                 unselectable: 'on'
20776             });
20777         };
20778         
20779         var header = {};
20780         
20781         if(!this.header){
20782             header = {
20783                 tag : 'table',
20784                 cls : 'fc-header',
20785                 style : 'width:100%',
20786                 cn : [
20787                     {
20788                         tag: 'tr',
20789                         cn : [
20790                             {
20791                                 tag : 'td',
20792                                 cls : 'fc-header-left',
20793                                 cn : [
20794                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20795                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20796                                     { tag: 'span', cls: 'fc-header-space' },
20797                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20798
20799
20800                                 ]
20801                             },
20802
20803                             {
20804                                 tag : 'td',
20805                                 cls : 'fc-header-center',
20806                                 cn : [
20807                                     {
20808                                         tag: 'span',
20809                                         cls: 'fc-header-title',
20810                                         cn : {
20811                                             tag: 'H2',
20812                                             html : 'month / year'
20813                                         }
20814                                     }
20815
20816                                 ]
20817                             },
20818                             {
20819                                 tag : 'td',
20820                                 cls : 'fc-header-right',
20821                                 cn : [
20822                               /*      fc_button('month', 'left', '', 'month' ),
20823                                     fc_button('week', '', '', 'week' ),
20824                                     fc_button('day', 'right', '', 'day' )
20825                                 */    
20826
20827                                 ]
20828                             }
20829
20830                         ]
20831                     }
20832                 ]
20833             };
20834         }
20835         
20836         header = this.header;
20837         
20838        
20839         var cal_heads = function() {
20840             var ret = [];
20841             // fixme - handle this.
20842             
20843             for (var i =0; i < Date.dayNames.length; i++) {
20844                 var d = Date.dayNames[i];
20845                 ret.push({
20846                     tag: 'th',
20847                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20848                     html : d.substring(0,3)
20849                 });
20850                 
20851             }
20852             ret[0].cls += ' fc-first';
20853             ret[6].cls += ' fc-last';
20854             return ret;
20855         };
20856         var cal_cell = function(n) {
20857             return  {
20858                 tag: 'td',
20859                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20860                 cn : [
20861                     {
20862                         cn : [
20863                             {
20864                                 cls: 'fc-day-number',
20865                                 html: 'D'
20866                             },
20867                             {
20868                                 cls: 'fc-day-content',
20869                              
20870                                 cn : [
20871                                      {
20872                                         style: 'position: relative;' // height: 17px;
20873                                     }
20874                                 ]
20875                             }
20876                             
20877                             
20878                         ]
20879                     }
20880                 ]
20881                 
20882             }
20883         };
20884         var cal_rows = function() {
20885             
20886             var ret = [];
20887             for (var r = 0; r < 6; r++) {
20888                 var row= {
20889                     tag : 'tr',
20890                     cls : 'fc-week',
20891                     cn : []
20892                 };
20893                 
20894                 for (var i =0; i < Date.dayNames.length; i++) {
20895                     var d = Date.dayNames[i];
20896                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20897
20898                 }
20899                 row.cn[0].cls+=' fc-first';
20900                 row.cn[0].cn[0].style = 'min-height:90px';
20901                 row.cn[6].cls+=' fc-last';
20902                 ret.push(row);
20903                 
20904             }
20905             ret[0].cls += ' fc-first';
20906             ret[4].cls += ' fc-prev-last';
20907             ret[5].cls += ' fc-last';
20908             return ret;
20909             
20910         };
20911         
20912         var cal_table = {
20913             tag: 'table',
20914             cls: 'fc-border-separate',
20915             style : 'width:100%',
20916             cellspacing  : 0,
20917             cn : [
20918                 { 
20919                     tag: 'thead',
20920                     cn : [
20921                         { 
20922                             tag: 'tr',
20923                             cls : 'fc-first fc-last',
20924                             cn : cal_heads()
20925                         }
20926                     ]
20927                 },
20928                 { 
20929                     tag: 'tbody',
20930                     cn : cal_rows()
20931                 }
20932                   
20933             ]
20934         };
20935          
20936          var cfg = {
20937             cls : 'fc fc-ltr',
20938             cn : [
20939                 header,
20940                 {
20941                     cls : 'fc-content',
20942                     style : "position: relative;",
20943                     cn : [
20944                         {
20945                             cls : 'fc-view fc-view-month fc-grid',
20946                             style : 'position: relative',
20947                             unselectable : 'on',
20948                             cn : [
20949                                 {
20950                                     cls : 'fc-event-container',
20951                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20952                                 },
20953                                 cal_table
20954                             ]
20955                         }
20956                     ]
20957     
20958                 }
20959            ] 
20960             
20961         };
20962         
20963          
20964         
20965         return cfg;
20966     },
20967     
20968     
20969     initEvents : function()
20970     {
20971         if(!this.store){
20972             throw "can not find store for calendar";
20973         }
20974         
20975         var mark = {
20976             tag: "div",
20977             cls:"x-dlg-mask",
20978             style: "text-align:center",
20979             cn: [
20980                 {
20981                     tag: "div",
20982                     style: "background-color:white;width:50%;margin:250 auto",
20983                     cn: [
20984                         {
20985                             tag: "img",
20986                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20987                         },
20988                         {
20989                             tag: "span",
20990                             html: "Loading"
20991                         }
20992                         
20993                     ]
20994                 }
20995             ]
20996         };
20997         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20998         
20999         var size = this.el.select('.fc-content', true).first().getSize();
21000         this.maskEl.setSize(size.width, size.height);
21001         this.maskEl.enableDisplayMode("block");
21002         if(!this.loadMask){
21003             this.maskEl.hide();
21004         }
21005         
21006         this.store = Roo.factory(this.store, Roo.data);
21007         this.store.on('load', this.onLoad, this);
21008         this.store.on('beforeload', this.onBeforeLoad, this);
21009         
21010         this.resize();
21011         
21012         this.cells = this.el.select('.fc-day',true);
21013         //Roo.log(this.cells);
21014         this.textNodes = this.el.query('.fc-day-number');
21015         this.cells.addClassOnOver('fc-state-hover');
21016         
21017         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
21018         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
21019         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
21020         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
21021         
21022         this.on('monthchange', this.onMonthChange, this);
21023         
21024         this.update(new Date().clearTime());
21025     },
21026     
21027     resize : function() {
21028         var sz  = this.el.getSize();
21029         
21030         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21031         this.el.select('.fc-day-content div',true).setHeight(34);
21032     },
21033     
21034     
21035     // private
21036     showPrevMonth : function(e){
21037         this.update(this.activeDate.add("mo", -1));
21038     },
21039     showToday : function(e){
21040         this.update(new Date().clearTime());
21041     },
21042     // private
21043     showNextMonth : function(e){
21044         this.update(this.activeDate.add("mo", 1));
21045     },
21046
21047     // private
21048     showPrevYear : function(){
21049         this.update(this.activeDate.add("y", -1));
21050     },
21051
21052     // private
21053     showNextYear : function(){
21054         this.update(this.activeDate.add("y", 1));
21055     },
21056
21057     
21058    // private
21059     update : function(date)
21060     {
21061         var vd = this.activeDate;
21062         this.activeDate = date;
21063 //        if(vd && this.el){
21064 //            var t = date.getTime();
21065 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21066 //                Roo.log('using add remove');
21067 //                
21068 //                this.fireEvent('monthchange', this, date);
21069 //                
21070 //                this.cells.removeClass("fc-state-highlight");
21071 //                this.cells.each(function(c){
21072 //                   if(c.dateValue == t){
21073 //                       c.addClass("fc-state-highlight");
21074 //                       setTimeout(function(){
21075 //                            try{c.dom.firstChild.focus();}catch(e){}
21076 //                       }, 50);
21077 //                       return false;
21078 //                   }
21079 //                   return true;
21080 //                });
21081 //                return;
21082 //            }
21083 //        }
21084         
21085         var days = date.getDaysInMonth();
21086         
21087         var firstOfMonth = date.getFirstDateOfMonth();
21088         var startingPos = firstOfMonth.getDay()-this.startDay;
21089         
21090         if(startingPos < this.startDay){
21091             startingPos += 7;
21092         }
21093         
21094         var pm = date.add(Date.MONTH, -1);
21095         var prevStart = pm.getDaysInMonth()-startingPos;
21096 //        
21097         this.cells = this.el.select('.fc-day',true);
21098         this.textNodes = this.el.query('.fc-day-number');
21099         this.cells.addClassOnOver('fc-state-hover');
21100         
21101         var cells = this.cells.elements;
21102         var textEls = this.textNodes;
21103         
21104         Roo.each(cells, function(cell){
21105             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21106         });
21107         
21108         days += startingPos;
21109
21110         // convert everything to numbers so it's fast
21111         var day = 86400000;
21112         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21113         //Roo.log(d);
21114         //Roo.log(pm);
21115         //Roo.log(prevStart);
21116         
21117         var today = new Date().clearTime().getTime();
21118         var sel = date.clearTime().getTime();
21119         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21120         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21121         var ddMatch = this.disabledDatesRE;
21122         var ddText = this.disabledDatesText;
21123         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21124         var ddaysText = this.disabledDaysText;
21125         var format = this.format;
21126         
21127         var setCellClass = function(cal, cell){
21128             cell.row = 0;
21129             cell.events = [];
21130             cell.more = [];
21131             //Roo.log('set Cell Class');
21132             cell.title = "";
21133             var t = d.getTime();
21134             
21135             //Roo.log(d);
21136             
21137             cell.dateValue = t;
21138             if(t == today){
21139                 cell.className += " fc-today";
21140                 cell.className += " fc-state-highlight";
21141                 cell.title = cal.todayText;
21142             }
21143             if(t == sel){
21144                 // disable highlight in other month..
21145                 //cell.className += " fc-state-highlight";
21146                 
21147             }
21148             // disabling
21149             if(t < min) {
21150                 cell.className = " fc-state-disabled";
21151                 cell.title = cal.minText;
21152                 return;
21153             }
21154             if(t > max) {
21155                 cell.className = " fc-state-disabled";
21156                 cell.title = cal.maxText;
21157                 return;
21158             }
21159             if(ddays){
21160                 if(ddays.indexOf(d.getDay()) != -1){
21161                     cell.title = ddaysText;
21162                     cell.className = " fc-state-disabled";
21163                 }
21164             }
21165             if(ddMatch && format){
21166                 var fvalue = d.dateFormat(format);
21167                 if(ddMatch.test(fvalue)){
21168                     cell.title = ddText.replace("%0", fvalue);
21169                     cell.className = " fc-state-disabled";
21170                 }
21171             }
21172             
21173             if (!cell.initialClassName) {
21174                 cell.initialClassName = cell.dom.className;
21175             }
21176             
21177             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21178         };
21179
21180         var i = 0;
21181         
21182         for(; i < startingPos; i++) {
21183             textEls[i].innerHTML = (++prevStart);
21184             d.setDate(d.getDate()+1);
21185             
21186             cells[i].className = "fc-past fc-other-month";
21187             setCellClass(this, cells[i]);
21188         }
21189         
21190         var intDay = 0;
21191         
21192         for(; i < days; i++){
21193             intDay = i - startingPos + 1;
21194             textEls[i].innerHTML = (intDay);
21195             d.setDate(d.getDate()+1);
21196             
21197             cells[i].className = ''; // "x-date-active";
21198             setCellClass(this, cells[i]);
21199         }
21200         var extraDays = 0;
21201         
21202         for(; i < 42; i++) {
21203             textEls[i].innerHTML = (++extraDays);
21204             d.setDate(d.getDate()+1);
21205             
21206             cells[i].className = "fc-future fc-other-month";
21207             setCellClass(this, cells[i]);
21208         }
21209         
21210         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21211         
21212         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21213         
21214         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21215         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21216         
21217         if(totalRows != 6){
21218             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21219             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21220         }
21221         
21222         this.fireEvent('monthchange', this, date);
21223         
21224         
21225         /*
21226         if(!this.internalRender){
21227             var main = this.el.dom.firstChild;
21228             var w = main.offsetWidth;
21229             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21230             Roo.fly(main).setWidth(w);
21231             this.internalRender = true;
21232             // opera does not respect the auto grow header center column
21233             // then, after it gets a width opera refuses to recalculate
21234             // without a second pass
21235             if(Roo.isOpera && !this.secondPass){
21236                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21237                 this.secondPass = true;
21238                 this.update.defer(10, this, [date]);
21239             }
21240         }
21241         */
21242         
21243     },
21244     
21245     findCell : function(dt) {
21246         dt = dt.clearTime().getTime();
21247         var ret = false;
21248         this.cells.each(function(c){
21249             //Roo.log("check " +c.dateValue + '?=' + dt);
21250             if(c.dateValue == dt){
21251                 ret = c;
21252                 return false;
21253             }
21254             return true;
21255         });
21256         
21257         return ret;
21258     },
21259     
21260     findCells : function(ev) {
21261         var s = ev.start.clone().clearTime().getTime();
21262        // Roo.log(s);
21263         var e= ev.end.clone().clearTime().getTime();
21264        // Roo.log(e);
21265         var ret = [];
21266         this.cells.each(function(c){
21267              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21268             
21269             if(c.dateValue > e){
21270                 return ;
21271             }
21272             if(c.dateValue < s){
21273                 return ;
21274             }
21275             ret.push(c);
21276         });
21277         
21278         return ret;    
21279     },
21280     
21281 //    findBestRow: function(cells)
21282 //    {
21283 //        var ret = 0;
21284 //        
21285 //        for (var i =0 ; i < cells.length;i++) {
21286 //            ret  = Math.max(cells[i].rows || 0,ret);
21287 //        }
21288 //        return ret;
21289 //        
21290 //    },
21291     
21292     
21293     addItem : function(ev)
21294     {
21295         // look for vertical location slot in
21296         var cells = this.findCells(ev);
21297         
21298 //        ev.row = this.findBestRow(cells);
21299         
21300         // work out the location.
21301         
21302         var crow = false;
21303         var rows = [];
21304         for(var i =0; i < cells.length; i++) {
21305             
21306             cells[i].row = cells[0].row;
21307             
21308             if(i == 0){
21309                 cells[i].row = cells[i].row + 1;
21310             }
21311             
21312             if (!crow) {
21313                 crow = {
21314                     start : cells[i],
21315                     end :  cells[i]
21316                 };
21317                 continue;
21318             }
21319             if (crow.start.getY() == cells[i].getY()) {
21320                 // on same row.
21321                 crow.end = cells[i];
21322                 continue;
21323             }
21324             // different row.
21325             rows.push(crow);
21326             crow = {
21327                 start: cells[i],
21328                 end : cells[i]
21329             };
21330             
21331         }
21332         
21333         rows.push(crow);
21334         ev.els = [];
21335         ev.rows = rows;
21336         ev.cells = cells;
21337         
21338         cells[0].events.push(ev);
21339         
21340         this.calevents.push(ev);
21341     },
21342     
21343     clearEvents: function() {
21344         
21345         if(!this.calevents){
21346             return;
21347         }
21348         
21349         Roo.each(this.cells.elements, function(c){
21350             c.row = 0;
21351             c.events = [];
21352             c.more = [];
21353         });
21354         
21355         Roo.each(this.calevents, function(e) {
21356             Roo.each(e.els, function(el) {
21357                 el.un('mouseenter' ,this.onEventEnter, this);
21358                 el.un('mouseleave' ,this.onEventLeave, this);
21359                 el.remove();
21360             },this);
21361         },this);
21362         
21363         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21364             e.remove();
21365         });
21366         
21367     },
21368     
21369     renderEvents: function()
21370     {   
21371         var _this = this;
21372         
21373         this.cells.each(function(c) {
21374             
21375             if(c.row < 5){
21376                 return;
21377             }
21378             
21379             var ev = c.events;
21380             
21381             var r = 4;
21382             if(c.row != c.events.length){
21383                 r = 4 - (4 - (c.row - c.events.length));
21384             }
21385             
21386             c.events = ev.slice(0, r);
21387             c.more = ev.slice(r);
21388             
21389             if(c.more.length && c.more.length == 1){
21390                 c.events.push(c.more.pop());
21391             }
21392             
21393             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21394             
21395         });
21396             
21397         this.cells.each(function(c) {
21398             
21399             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21400             
21401             
21402             for (var e = 0; e < c.events.length; e++){
21403                 var ev = c.events[e];
21404                 var rows = ev.rows;
21405                 
21406                 for(var i = 0; i < rows.length; i++) {
21407                 
21408                     // how many rows should it span..
21409
21410                     var  cfg = {
21411                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21412                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21413
21414                         unselectable : "on",
21415                         cn : [
21416                             {
21417                                 cls: 'fc-event-inner',
21418                                 cn : [
21419     //                                {
21420     //                                  tag:'span',
21421     //                                  cls: 'fc-event-time',
21422     //                                  html : cells.length > 1 ? '' : ev.time
21423     //                                },
21424                                     {
21425                                       tag:'span',
21426                                       cls: 'fc-event-title',
21427                                       html : String.format('{0}', ev.title)
21428                                     }
21429
21430
21431                                 ]
21432                             },
21433                             {
21434                                 cls: 'ui-resizable-handle ui-resizable-e',
21435                                 html : '&nbsp;&nbsp;&nbsp'
21436                             }
21437
21438                         ]
21439                     };
21440
21441                     if (i == 0) {
21442                         cfg.cls += ' fc-event-start';
21443                     }
21444                     if ((i+1) == rows.length) {
21445                         cfg.cls += ' fc-event-end';
21446                     }
21447
21448                     var ctr = _this.el.select('.fc-event-container',true).first();
21449                     var cg = ctr.createChild(cfg);
21450
21451                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21452                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21453
21454                     var r = (c.more.length) ? 1 : 0;
21455                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21456                     cg.setWidth(ebox.right - sbox.x -2);
21457
21458                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21459                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21460                     cg.on('click', _this.onEventClick, _this, ev);
21461
21462                     ev.els.push(cg);
21463                     
21464                 }
21465                 
21466             }
21467             
21468             
21469             if(c.more.length){
21470                 var  cfg = {
21471                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21472                     style : 'position: absolute',
21473                     unselectable : "on",
21474                     cn : [
21475                         {
21476                             cls: 'fc-event-inner',
21477                             cn : [
21478                                 {
21479                                   tag:'span',
21480                                   cls: 'fc-event-title',
21481                                   html : 'More'
21482                                 }
21483
21484
21485                             ]
21486                         },
21487                         {
21488                             cls: 'ui-resizable-handle ui-resizable-e',
21489                             html : '&nbsp;&nbsp;&nbsp'
21490                         }
21491
21492                     ]
21493                 };
21494
21495                 var ctr = _this.el.select('.fc-event-container',true).first();
21496                 var cg = ctr.createChild(cfg);
21497
21498                 var sbox = c.select('.fc-day-content',true).first().getBox();
21499                 var ebox = c.select('.fc-day-content',true).first().getBox();
21500                 //Roo.log(cg);
21501                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21502                 cg.setWidth(ebox.right - sbox.x -2);
21503
21504                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21505                 
21506             }
21507             
21508         });
21509         
21510         
21511         
21512     },
21513     
21514     onEventEnter: function (e, el,event,d) {
21515         this.fireEvent('evententer', this, el, event);
21516     },
21517     
21518     onEventLeave: function (e, el,event,d) {
21519         this.fireEvent('eventleave', this, el, event);
21520     },
21521     
21522     onEventClick: function (e, el,event,d) {
21523         this.fireEvent('eventclick', this, el, event);
21524     },
21525     
21526     onMonthChange: function () {
21527         this.store.load();
21528     },
21529     
21530     onMoreEventClick: function(e, el, more)
21531     {
21532         var _this = this;
21533         
21534         this.calpopover.placement = 'right';
21535         this.calpopover.setTitle('More');
21536         
21537         this.calpopover.setContent('');
21538         
21539         var ctr = this.calpopover.el.select('.popover-content', true).first();
21540         
21541         Roo.each(more, function(m){
21542             var cfg = {
21543                 cls : 'fc-event-hori fc-event-draggable',
21544                 html : m.title
21545             };
21546             var cg = ctr.createChild(cfg);
21547             
21548             cg.on('click', _this.onEventClick, _this, m);
21549         });
21550         
21551         this.calpopover.show(el);
21552         
21553         
21554     },
21555     
21556     onLoad: function () 
21557     {   
21558         this.calevents = [];
21559         var cal = this;
21560         
21561         if(this.store.getCount() > 0){
21562             this.store.data.each(function(d){
21563                cal.addItem({
21564                     id : d.data.id,
21565                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21566                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21567                     time : d.data.start_time,
21568                     title : d.data.title,
21569                     description : d.data.description,
21570                     venue : d.data.venue
21571                 });
21572             });
21573         }
21574         
21575         this.renderEvents();
21576         
21577         if(this.calevents.length && this.loadMask){
21578             this.maskEl.hide();
21579         }
21580     },
21581     
21582     onBeforeLoad: function()
21583     {
21584         this.clearEvents();
21585         if(this.loadMask){
21586             this.maskEl.show();
21587         }
21588     }
21589 });
21590
21591  
21592  /*
21593  * - LGPL
21594  *
21595  * element
21596  * 
21597  */
21598
21599 /**
21600  * @class Roo.bootstrap.Popover
21601  * @extends Roo.bootstrap.Component
21602  * @parent none builder
21603  * @children Roo.bootstrap.Component
21604  * Bootstrap Popover class
21605  * @cfg {String} html contents of the popover   (or false to use children..)
21606  * @cfg {String} title of popover (or false to hide)
21607  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21608  * @cfg {String} trigger click || hover (or false to trigger manually)
21609  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21610  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21611  *      - if false and it has a 'parent' then it will be automatically added to that element
21612  *      - if string - Roo.get  will be called 
21613  * @cfg {Number} delay - delay before showing
21614  
21615  * @constructor
21616  * Create a new Popover
21617  * @param {Object} config The config object
21618  */
21619
21620 Roo.bootstrap.Popover = function(config){
21621     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21622     
21623     this.addEvents({
21624         // raw events
21625          /**
21626          * @event show
21627          * After the popover show
21628          * 
21629          * @param {Roo.bootstrap.Popover} this
21630          */
21631         "show" : true,
21632         /**
21633          * @event hide
21634          * After the popover hide
21635          * 
21636          * @param {Roo.bootstrap.Popover} this
21637          */
21638         "hide" : true
21639     });
21640 };
21641
21642 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21643     
21644     title: false,
21645     html: false,
21646     
21647     placement : 'right',
21648     trigger : 'hover', // hover
21649     modal : false,
21650     delay : 0,
21651     
21652     over: false,
21653     
21654     can_build_overlaid : false,
21655     
21656     maskEl : false, // the mask element
21657     headerEl : false,
21658     contentEl : false,
21659     alignEl : false, // when show is called with an element - this get's stored.
21660     
21661     getChildContainer : function()
21662     {
21663         return this.contentEl;
21664         
21665     },
21666     getPopoverHeader : function()
21667     {
21668         this.title = true; // flag not to hide it..
21669         this.headerEl.addClass('p-0');
21670         return this.headerEl
21671     },
21672     
21673     
21674     getAutoCreate : function(){
21675          
21676         var cfg = {
21677            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21678            style: 'display:block',
21679            cn : [
21680                 {
21681                     cls : 'arrow'
21682                 },
21683                 {
21684                     cls : 'popover-inner ',
21685                     cn : [
21686                         {
21687                             tag: 'h3',
21688                             cls: 'popover-title popover-header',
21689                             html : this.title === false ? '' : this.title
21690                         },
21691                         {
21692                             cls : 'popover-content popover-body '  + (this.cls || ''),
21693                             html : this.html || ''
21694                         }
21695                     ]
21696                     
21697                 }
21698            ]
21699         };
21700         
21701         return cfg;
21702     },
21703     /**
21704      * @param {string} the title
21705      */
21706     setTitle: function(str)
21707     {
21708         this.title = str;
21709         if (this.el) {
21710             this.headerEl.dom.innerHTML = str;
21711         }
21712         
21713     },
21714     /**
21715      * @param {string} the body content
21716      */
21717     setContent: function(str)
21718     {
21719         this.html = str;
21720         if (this.contentEl) {
21721             this.contentEl.dom.innerHTML = str;
21722         }
21723         
21724     },
21725     // as it get's added to the bottom of the page.
21726     onRender : function(ct, position)
21727     {
21728         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21729         
21730         
21731         
21732         if(!this.el){
21733             var cfg = Roo.apply({},  this.getAutoCreate());
21734             cfg.id = Roo.id();
21735             
21736             if (this.cls) {
21737                 cfg.cls += ' ' + this.cls;
21738             }
21739             if (this.style) {
21740                 cfg.style = this.style;
21741             }
21742             //Roo.log("adding to ");
21743             this.el = Roo.get(document.body).createChild(cfg, position);
21744 //            Roo.log(this.el);
21745         }
21746         
21747         this.contentEl = this.el.select('.popover-content',true).first();
21748         this.headerEl =  this.el.select('.popover-title',true).first();
21749         
21750         var nitems = [];
21751         if(typeof(this.items) != 'undefined'){
21752             var items = this.items;
21753             delete this.items;
21754
21755             for(var i =0;i < items.length;i++) {
21756                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21757             }
21758         }
21759
21760         this.items = nitems;
21761         
21762         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21763         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21764         
21765         
21766         
21767         this.initEvents();
21768     },
21769     
21770     resizeMask : function()
21771     {
21772         this.maskEl.setSize(
21773             Roo.lib.Dom.getViewWidth(true),
21774             Roo.lib.Dom.getViewHeight(true)
21775         );
21776     },
21777     
21778     initEvents : function()
21779     {
21780         
21781         if (!this.modal) { 
21782             Roo.bootstrap.Popover.register(this);
21783         }
21784          
21785         this.arrowEl = this.el.select('.arrow',true).first();
21786         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21787         this.el.enableDisplayMode('block');
21788         this.el.hide();
21789  
21790         
21791         if (this.over === false && !this.parent()) {
21792             return; 
21793         }
21794         if (this.triggers === false) {
21795             return;
21796         }
21797          
21798         // support parent
21799         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21800         var triggers = this.trigger ? this.trigger.split(' ') : [];
21801         Roo.each(triggers, function(trigger) {
21802         
21803             if (trigger == 'click') {
21804                 on_el.on('click', this.toggle, this);
21805             } else if (trigger != 'manual') {
21806                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21807                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21808       
21809                 on_el.on(eventIn  ,this.enter, this);
21810                 on_el.on(eventOut, this.leave, this);
21811             }
21812         }, this);
21813     },
21814     
21815     
21816     // private
21817     timeout : null,
21818     hoverState : null,
21819     
21820     toggle : function () {
21821         this.hoverState == 'in' ? this.leave() : this.enter();
21822     },
21823     
21824     enter : function () {
21825         
21826         clearTimeout(this.timeout);
21827     
21828         this.hoverState = 'in';
21829     
21830         if (!this.delay || !this.delay.show) {
21831             this.show();
21832             return;
21833         }
21834         var _t = this;
21835         this.timeout = setTimeout(function () {
21836             if (_t.hoverState == 'in') {
21837                 _t.show();
21838             }
21839         }, this.delay.show)
21840     },
21841     
21842     leave : function() {
21843         clearTimeout(this.timeout);
21844     
21845         this.hoverState = 'out';
21846     
21847         if (!this.delay || !this.delay.hide) {
21848             this.hide();
21849             return;
21850         }
21851         var _t = this;
21852         this.timeout = setTimeout(function () {
21853             if (_t.hoverState == 'out') {
21854                 _t.hide();
21855             }
21856         }, this.delay.hide)
21857     },
21858     
21859     /**
21860      * update the position of the dialog
21861      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21862      * 
21863      *
21864      */
21865     
21866     doAlign : function()
21867     {
21868         
21869         if (this.alignEl) {
21870             this.updatePosition(this.placement, true);
21871              
21872         } else {
21873             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21874             var es = this.el.getSize();
21875             var x = Roo.lib.Dom.getViewWidth()/2;
21876             var y = Roo.lib.Dom.getViewHeight()/2;
21877             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21878             
21879         }
21880
21881          
21882          
21883         
21884         
21885     },
21886     
21887     /**
21888      * Show the popover
21889      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21890      * @param {string} (left|right|top|bottom) position
21891      */
21892     show : function (on_el, placement)
21893     {
21894         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21895         on_el = on_el || false; // default to false
21896          
21897         if (!on_el) {
21898             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21899                 on_el = this.parent().el;
21900             } else if (this.over) {
21901                 on_el = Roo.get(this.over);
21902             }
21903             
21904         }
21905         
21906         this.alignEl = Roo.get( on_el );
21907
21908         if (!this.el) {
21909             this.render(document.body);
21910         }
21911         
21912         
21913          
21914         
21915         if (this.title === false) {
21916             this.headerEl.hide();
21917         }
21918         
21919        
21920         this.el.show();
21921         this.el.dom.style.display = 'block';
21922          
21923         this.doAlign();
21924         
21925         //var arrow = this.el.select('.arrow',true).first();
21926         //arrow.set(align[2], 
21927         
21928         this.el.addClass('in');
21929         
21930          
21931         
21932         this.hoverState = 'in';
21933         
21934         if (this.modal) {
21935             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21936             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21937             this.maskEl.dom.style.display = 'block';
21938             this.maskEl.addClass('show');
21939         }
21940         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21941  
21942         this.fireEvent('show', this);
21943         
21944     },
21945     /**
21946      * fire this manually after loading a grid in the table for example
21947      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21948      * @param {Boolean} try and move it if we cant get right position.
21949      */
21950     updatePosition : function(placement, try_move)
21951     {
21952         // allow for calling with no parameters
21953         placement = placement   ? placement :  this.placement;
21954         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21955         
21956         this.el.removeClass([
21957             'fade','top','bottom', 'left', 'right','in',
21958             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21959         ]);
21960         this.el.addClass(placement + ' bs-popover-' + placement);
21961         
21962         if (!this.alignEl ) {
21963             return false;
21964         }
21965         
21966         switch (placement) {
21967             case 'right':
21968                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21969                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21970                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21971                     //normal display... or moved up/down.
21972                     this.el.setXY(offset);
21973                     var xy = this.alignEl.getAnchorXY('tr', false);
21974                     xy[0]+=2;xy[1]+=5;
21975                     this.arrowEl.setXY(xy);
21976                     return true;
21977                 }
21978                 // continue through...
21979                 return this.updatePosition('left', false);
21980                 
21981             
21982             case 'left':
21983                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21984                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21985                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21986                     //normal display... or moved up/down.
21987                     this.el.setXY(offset);
21988                     var xy = this.alignEl.getAnchorXY('tl', false);
21989                     xy[0]-=10;xy[1]+=5; // << fix me
21990                     this.arrowEl.setXY(xy);
21991                     return true;
21992                 }
21993                 // call self...
21994                 return this.updatePosition('right', false);
21995             
21996             case 'top':
21997                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21998                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21999                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
22000                     //normal display... or moved up/down.
22001                     this.el.setXY(offset);
22002                     var xy = this.alignEl.getAnchorXY('t', false);
22003                     xy[1]-=10; // << fix me
22004                     this.arrowEl.setXY(xy);
22005                     return true;
22006                 }
22007                 // fall through
22008                return this.updatePosition('bottom', false);
22009             
22010             case 'bottom':
22011                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
22012                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
22013                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
22014                     //normal display... or moved up/down.
22015                     this.el.setXY(offset);
22016                     var xy = this.alignEl.getAnchorXY('b', false);
22017                      xy[1]+=2; // << fix me
22018                     this.arrowEl.setXY(xy);
22019                     return true;
22020                 }
22021                 // fall through
22022                 return this.updatePosition('top', false);
22023                 
22024             
22025         }
22026         
22027         
22028         return false;
22029     },
22030     
22031     hide : function()
22032     {
22033         this.el.setXY([0,0]);
22034         this.el.removeClass('in');
22035         this.el.hide();
22036         this.hoverState = null;
22037         this.maskEl.hide(); // always..
22038         this.fireEvent('hide', this);
22039     }
22040     
22041 });
22042
22043
22044 Roo.apply(Roo.bootstrap.Popover, {
22045
22046     alignment : {
22047         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22048         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22049         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22050         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22051     },
22052     
22053     zIndex : 20001,
22054
22055     clickHander : false,
22056     
22057     
22058
22059     onMouseDown : function(e)
22060     {
22061         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22062             /// what is nothing is showing..
22063             this.hideAll();
22064         }
22065          
22066     },
22067     
22068     
22069     popups : [],
22070     
22071     register : function(popup)
22072     {
22073         if (!Roo.bootstrap.Popover.clickHandler) {
22074             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22075         }
22076         // hide other popups.
22077         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22078         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22079         this.hideAll(); //<< why?
22080         //this.popups.push(popup);
22081     },
22082     hideAll : function()
22083     {
22084         this.popups.forEach(function(p) {
22085             p.hide();
22086         });
22087     },
22088     onShow : function() {
22089         Roo.bootstrap.Popover.popups.push(this);
22090     },
22091     onHide : function() {
22092         Roo.bootstrap.Popover.popups.remove(this);
22093     } 
22094
22095 });
22096 /**
22097  * @class Roo.bootstrap.PopoverNav
22098  * @extends Roo.bootstrap.nav.Simplebar
22099  * @parent Roo.bootstrap.Popover
22100  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22101  * @licence LGPL
22102  * Bootstrap Popover header navigation class
22103  * FIXME? should this go under nav?
22104  *
22105  * 
22106  * @constructor
22107  * Create a new Popover Header Navigation 
22108  * @param {Object} config The config object
22109  */
22110
22111 Roo.bootstrap.PopoverNav = function(config){
22112     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22113 };
22114
22115 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22116     
22117     
22118     container_method : 'getPopoverHeader' 
22119     
22120      
22121     
22122     
22123    
22124 });
22125
22126  
22127
22128  /*
22129  * - LGPL
22130  *
22131  * Progress
22132  * 
22133  */
22134
22135 /**
22136  * @class Roo.bootstrap.Progress
22137  * @extends Roo.bootstrap.Component
22138  * @children Roo.bootstrap.ProgressBar
22139  * Bootstrap Progress class
22140  * @cfg {Boolean} striped striped of the progress bar
22141  * @cfg {Boolean} active animated of the progress bar
22142  * 
22143  * 
22144  * @constructor
22145  * Create a new Progress
22146  * @param {Object} config The config object
22147  */
22148
22149 Roo.bootstrap.Progress = function(config){
22150     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22151 };
22152
22153 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22154     
22155     striped : false,
22156     active: false,
22157     
22158     getAutoCreate : function(){
22159         var cfg = {
22160             tag: 'div',
22161             cls: 'progress'
22162         };
22163         
22164         
22165         if(this.striped){
22166             cfg.cls += ' progress-striped';
22167         }
22168       
22169         if(this.active){
22170             cfg.cls += ' active';
22171         }
22172         
22173         
22174         return cfg;
22175     }
22176    
22177 });
22178
22179  
22180
22181  /*
22182  * - LGPL
22183  *
22184  * ProgressBar
22185  * 
22186  */
22187
22188 /**
22189  * @class Roo.bootstrap.ProgressBar
22190  * @extends Roo.bootstrap.Component
22191  * Bootstrap ProgressBar class
22192  * @cfg {Number} aria_valuenow aria-value now
22193  * @cfg {Number} aria_valuemin aria-value min
22194  * @cfg {Number} aria_valuemax aria-value max
22195  * @cfg {String} label label for the progress bar
22196  * @cfg {String} panel (success | info | warning | danger )
22197  * @cfg {String} role role of the progress bar
22198  * @cfg {String} sr_only text
22199  * 
22200  * 
22201  * @constructor
22202  * Create a new ProgressBar
22203  * @param {Object} config The config object
22204  */
22205
22206 Roo.bootstrap.ProgressBar = function(config){
22207     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22208 };
22209
22210 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22211     
22212     aria_valuenow : 0,
22213     aria_valuemin : 0,
22214     aria_valuemax : 100,
22215     label : false,
22216     panel : false,
22217     role : false,
22218     sr_only: false,
22219     
22220     getAutoCreate : function()
22221     {
22222         
22223         var cfg = {
22224             tag: 'div',
22225             cls: 'progress-bar',
22226             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22227         };
22228         
22229         if(this.sr_only){
22230             cfg.cn = {
22231                 tag: 'span',
22232                 cls: 'sr-only',
22233                 html: this.sr_only
22234             }
22235         }
22236         
22237         if(this.role){
22238             cfg.role = this.role;
22239         }
22240         
22241         if(this.aria_valuenow){
22242             cfg['aria-valuenow'] = this.aria_valuenow;
22243         }
22244         
22245         if(this.aria_valuemin){
22246             cfg['aria-valuemin'] = this.aria_valuemin;
22247         }
22248         
22249         if(this.aria_valuemax){
22250             cfg['aria-valuemax'] = this.aria_valuemax;
22251         }
22252         
22253         if(this.label && !this.sr_only){
22254             cfg.html = this.label;
22255         }
22256         
22257         if(this.panel){
22258             cfg.cls += ' progress-bar-' + this.panel;
22259         }
22260         
22261         return cfg;
22262     },
22263     
22264     update : function(aria_valuenow)
22265     {
22266         this.aria_valuenow = aria_valuenow;
22267         
22268         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22269     }
22270    
22271 });
22272
22273  
22274
22275  /**
22276  * @class Roo.bootstrap.TabGroup
22277  * @extends Roo.bootstrap.Column
22278  * @children Roo.bootstrap.TabPanel
22279  * Bootstrap Column class
22280  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22281  * @cfg {Boolean} carousel true to make the group behave like a carousel
22282  * @cfg {Boolean} bullets show bullets for the panels
22283  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22284  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22285  * @cfg {Boolean} showarrow (true|false) show arrow default true
22286  * 
22287  * @constructor
22288  * Create a new TabGroup
22289  * @param {Object} config The config object
22290  */
22291
22292 Roo.bootstrap.TabGroup = function(config){
22293     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22294     if (!this.navId) {
22295         this.navId = Roo.id();
22296     }
22297     this.tabs = [];
22298     Roo.bootstrap.TabGroup.register(this);
22299     
22300 };
22301
22302 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22303     
22304     carousel : false,
22305     transition : false,
22306     bullets : 0,
22307     timer : 0,
22308     autoslide : false,
22309     slideFn : false,
22310     slideOnTouch : false,
22311     showarrow : true,
22312     
22313     getAutoCreate : function()
22314     {
22315         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22316         
22317         cfg.cls += ' tab-content';
22318         
22319         if (this.carousel) {
22320             cfg.cls += ' carousel slide';
22321             
22322             cfg.cn = [{
22323                cls : 'carousel-inner',
22324                cn : []
22325             }];
22326         
22327             if(this.bullets  && !Roo.isTouch){
22328                 
22329                 var bullets = {
22330                     cls : 'carousel-bullets',
22331                     cn : []
22332                 };
22333                
22334                 if(this.bullets_cls){
22335                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22336                 }
22337                 
22338                 bullets.cn.push({
22339                     cls : 'clear'
22340                 });
22341                 
22342                 cfg.cn[0].cn.push(bullets);
22343             }
22344             
22345             if(this.showarrow){
22346                 cfg.cn[0].cn.push({
22347                     tag : 'div',
22348                     class : 'carousel-arrow',
22349                     cn : [
22350                         {
22351                             tag : 'div',
22352                             class : 'carousel-prev',
22353                             cn : [
22354                                 {
22355                                     tag : 'i',
22356                                     class : 'fa fa-chevron-left'
22357                                 }
22358                             ]
22359                         },
22360                         {
22361                             tag : 'div',
22362                             class : 'carousel-next',
22363                             cn : [
22364                                 {
22365                                     tag : 'i',
22366                                     class : 'fa fa-chevron-right'
22367                                 }
22368                             ]
22369                         }
22370                     ]
22371                 });
22372             }
22373             
22374         }
22375         
22376         return cfg;
22377     },
22378     
22379     initEvents:  function()
22380     {
22381 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22382 //            this.el.on("touchstart", this.onTouchStart, this);
22383 //        }
22384         
22385         if(this.autoslide){
22386             var _this = this;
22387             
22388             this.slideFn = window.setInterval(function() {
22389                 _this.showPanelNext();
22390             }, this.timer);
22391         }
22392         
22393         if(this.showarrow){
22394             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22395             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22396         }
22397         
22398         
22399     },
22400     
22401 //    onTouchStart : function(e, el, o)
22402 //    {
22403 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22404 //            return;
22405 //        }
22406 //        
22407 //        this.showPanelNext();
22408 //    },
22409     
22410     
22411     getChildContainer : function()
22412     {
22413         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22414     },
22415     
22416     /**
22417     * register a Navigation item
22418     * @param {Roo.bootstrap.nav.Item} the navitem to add
22419     */
22420     register : function(item)
22421     {
22422         this.tabs.push( item);
22423         item.navId = this.navId; // not really needed..
22424         this.addBullet();
22425     
22426     },
22427     
22428     getActivePanel : function()
22429     {
22430         var r = false;
22431         Roo.each(this.tabs, function(t) {
22432             if (t.active) {
22433                 r = t;
22434                 return false;
22435             }
22436             return null;
22437         });
22438         return r;
22439         
22440     },
22441     getPanelByName : function(n)
22442     {
22443         var r = false;
22444         Roo.each(this.tabs, function(t) {
22445             if (t.tabId == n) {
22446                 r = t;
22447                 return false;
22448             }
22449             return null;
22450         });
22451         return r;
22452     },
22453     indexOfPanel : function(p)
22454     {
22455         var r = false;
22456         Roo.each(this.tabs, function(t,i) {
22457             if (t.tabId == p.tabId) {
22458                 r = i;
22459                 return false;
22460             }
22461             return null;
22462         });
22463         return r;
22464     },
22465     /**
22466      * show a specific panel
22467      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22468      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22469      */
22470     showPanel : function (pan)
22471     {
22472         if(this.transition || typeof(pan) == 'undefined'){
22473             Roo.log("waiting for the transitionend");
22474             return false;
22475         }
22476         
22477         if (typeof(pan) == 'number') {
22478             pan = this.tabs[pan];
22479         }
22480         
22481         if (typeof(pan) == 'string') {
22482             pan = this.getPanelByName(pan);
22483         }
22484         
22485         var cur = this.getActivePanel();
22486         
22487         if(!pan || !cur){
22488             Roo.log('pan or acitve pan is undefined');
22489             return false;
22490         }
22491         
22492         if (pan.tabId == this.getActivePanel().tabId) {
22493             return true;
22494         }
22495         
22496         if (false === cur.fireEvent('beforedeactivate')) {
22497             return false;
22498         }
22499         
22500         if(this.bullets > 0 && !Roo.isTouch){
22501             this.setActiveBullet(this.indexOfPanel(pan));
22502         }
22503         
22504         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22505             
22506             //class="carousel-item carousel-item-next carousel-item-left"
22507             
22508             this.transition = true;
22509             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22510             var lr = dir == 'next' ? 'left' : 'right';
22511             pan.el.addClass(dir); // or prev
22512             pan.el.addClass('carousel-item-' + dir); // or prev
22513             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22514             cur.el.addClass(lr); // or right
22515             pan.el.addClass(lr);
22516             cur.el.addClass('carousel-item-' +lr); // or right
22517             pan.el.addClass('carousel-item-' +lr);
22518             
22519             
22520             var _this = this;
22521             cur.el.on('transitionend', function() {
22522                 Roo.log("trans end?");
22523                 
22524                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22525                 pan.setActive(true);
22526                 
22527                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22528                 cur.setActive(false);
22529                 
22530                 _this.transition = false;
22531                 
22532             }, this, { single:  true } );
22533             
22534             return true;
22535         }
22536         
22537         cur.setActive(false);
22538         pan.setActive(true);
22539         
22540         return true;
22541         
22542     },
22543     showPanelNext : function()
22544     {
22545         var i = this.indexOfPanel(this.getActivePanel());
22546         
22547         if (i >= this.tabs.length - 1 && !this.autoslide) {
22548             return;
22549         }
22550         
22551         if (i >= this.tabs.length - 1 && this.autoslide) {
22552             i = -1;
22553         }
22554         
22555         this.showPanel(this.tabs[i+1]);
22556     },
22557     
22558     showPanelPrev : function()
22559     {
22560         var i = this.indexOfPanel(this.getActivePanel());
22561         
22562         if (i  < 1 && !this.autoslide) {
22563             return;
22564         }
22565         
22566         if (i < 1 && this.autoslide) {
22567             i = this.tabs.length;
22568         }
22569         
22570         this.showPanel(this.tabs[i-1]);
22571     },
22572     
22573     
22574     addBullet: function()
22575     {
22576         if(!this.bullets || Roo.isTouch){
22577             return;
22578         }
22579         var ctr = this.el.select('.carousel-bullets',true).first();
22580         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22581         var bullet = ctr.createChild({
22582             cls : 'bullet bullet-' + i
22583         },ctr.dom.lastChild);
22584         
22585         
22586         var _this = this;
22587         
22588         bullet.on('click', (function(e, el, o, ii, t){
22589
22590             e.preventDefault();
22591
22592             this.showPanel(ii);
22593
22594             if(this.autoslide && this.slideFn){
22595                 clearInterval(this.slideFn);
22596                 this.slideFn = window.setInterval(function() {
22597                     _this.showPanelNext();
22598                 }, this.timer);
22599             }
22600
22601         }).createDelegate(this, [i, bullet], true));
22602                 
22603         
22604     },
22605      
22606     setActiveBullet : function(i)
22607     {
22608         if(Roo.isTouch){
22609             return;
22610         }
22611         
22612         Roo.each(this.el.select('.bullet', true).elements, function(el){
22613             el.removeClass('selected');
22614         });
22615
22616         var bullet = this.el.select('.bullet-' + i, true).first();
22617         
22618         if(!bullet){
22619             return;
22620         }
22621         
22622         bullet.addClass('selected');
22623     }
22624     
22625     
22626   
22627 });
22628
22629  
22630
22631  
22632  
22633 Roo.apply(Roo.bootstrap.TabGroup, {
22634     
22635     groups: {},
22636      /**
22637     * register a Navigation Group
22638     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22639     */
22640     register : function(navgrp)
22641     {
22642         this.groups[navgrp.navId] = navgrp;
22643         
22644     },
22645     /**
22646     * fetch a Navigation Group based on the navigation ID
22647     * if one does not exist , it will get created.
22648     * @param {string} the navgroup to add
22649     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22650     */
22651     get: function(navId) {
22652         if (typeof(this.groups[navId]) == 'undefined') {
22653             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22654         }
22655         return this.groups[navId] ;
22656     }
22657     
22658     
22659     
22660 });
22661
22662  /*
22663  * - LGPL
22664  *
22665  * TabPanel
22666  * 
22667  */
22668
22669 /**
22670  * @class Roo.bootstrap.TabPanel
22671  * @extends Roo.bootstrap.Component
22672  * @children Roo.bootstrap.Component
22673  * Bootstrap TabPanel class
22674  * @cfg {Boolean} active panel active
22675  * @cfg {String} html panel content
22676  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22677  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22678  * @cfg {String} href click to link..
22679  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22680  * 
22681  * 
22682  * @constructor
22683  * Create a new TabPanel
22684  * @param {Object} config The config object
22685  */
22686
22687 Roo.bootstrap.TabPanel = function(config){
22688     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22689     this.addEvents({
22690         /**
22691              * @event changed
22692              * Fires when the active status changes
22693              * @param {Roo.bootstrap.TabPanel} this
22694              * @param {Boolean} state the new state
22695             
22696          */
22697         'changed': true,
22698         /**
22699              * @event beforedeactivate
22700              * Fires before a tab is de-activated - can be used to do validation on a form.
22701              * @param {Roo.bootstrap.TabPanel} this
22702              * @return {Boolean} false if there is an error
22703             
22704          */
22705         'beforedeactivate': true
22706      });
22707     
22708     this.tabId = this.tabId || Roo.id();
22709   
22710 };
22711
22712 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22713     
22714     active: false,
22715     html: false,
22716     tabId: false,
22717     navId : false,
22718     href : '',
22719     touchSlide : false,
22720     getAutoCreate : function(){
22721         
22722         
22723         var cfg = {
22724             tag: 'div',
22725             // item is needed for carousel - not sure if it has any effect otherwise
22726             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22727             html: this.html || ''
22728         };
22729         
22730         if(this.active){
22731             cfg.cls += ' active';
22732         }
22733         
22734         if(this.tabId){
22735             cfg.tabId = this.tabId;
22736         }
22737         
22738         
22739         
22740         return cfg;
22741     },
22742     
22743     initEvents:  function()
22744     {
22745         var p = this.parent();
22746         
22747         this.navId = this.navId || p.navId;
22748         
22749         if (typeof(this.navId) != 'undefined') {
22750             // not really needed.. but just in case.. parent should be a NavGroup.
22751             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22752             
22753             tg.register(this);
22754             
22755             var i = tg.tabs.length - 1;
22756             
22757             if(this.active && tg.bullets > 0 && i < tg.bullets){
22758                 tg.setActiveBullet(i);
22759             }
22760         }
22761         
22762         this.el.on('click', this.onClick, this);
22763         
22764         if(Roo.isTouch && this.touchSlide){
22765             this.el.on("touchstart", this.onTouchStart, this);
22766             this.el.on("touchmove", this.onTouchMove, this);
22767             this.el.on("touchend", this.onTouchEnd, this);
22768         }
22769         
22770     },
22771     
22772     onRender : function(ct, position)
22773     {
22774         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22775     },
22776     
22777     setActive : function(state)
22778     {
22779         Roo.log("panel - set active " + this.tabId + "=" + state);
22780         
22781         this.active = state;
22782         if (!state) {
22783             this.el.removeClass('active');
22784             
22785         } else  if (!this.el.hasClass('active')) {
22786             this.el.addClass('active');
22787         }
22788         
22789         this.fireEvent('changed', this, state);
22790     },
22791     
22792     onClick : function(e)
22793     {
22794         e.preventDefault();
22795         
22796         if(!this.href.length){
22797             return;
22798         }
22799         
22800         window.location.href = this.href;
22801     },
22802     
22803     startX : 0,
22804     startY : 0,
22805     endX : 0,
22806     endY : 0,
22807     swiping : false,
22808     
22809     onTouchStart : function(e)
22810     {
22811         this.swiping = false;
22812         
22813         this.startX = e.browserEvent.touches[0].clientX;
22814         this.startY = e.browserEvent.touches[0].clientY;
22815     },
22816     
22817     onTouchMove : function(e)
22818     {
22819         this.swiping = true;
22820         
22821         this.endX = e.browserEvent.touches[0].clientX;
22822         this.endY = e.browserEvent.touches[0].clientY;
22823     },
22824     
22825     onTouchEnd : function(e)
22826     {
22827         if(!this.swiping){
22828             this.onClick(e);
22829             return;
22830         }
22831         
22832         var tabGroup = this.parent();
22833         
22834         if(this.endX > this.startX){ // swiping right
22835             tabGroup.showPanelPrev();
22836             return;
22837         }
22838         
22839         if(this.startX > this.endX){ // swiping left
22840             tabGroup.showPanelNext();
22841             return;
22842         }
22843     }
22844     
22845     
22846 });
22847  
22848
22849  
22850
22851  /*
22852  * - LGPL
22853  *
22854  * DateField
22855  * 
22856  */
22857
22858 /**
22859  * @class Roo.bootstrap.form.DateField
22860  * @extends Roo.bootstrap.form.Input
22861  * Bootstrap DateField class
22862  * @cfg {Number} weekStart default 0
22863  * @cfg {String} viewMode default empty, (months|years)
22864  * @cfg {String} minViewMode default empty, (months|years)
22865  * @cfg {Number} startDate default -Infinity
22866  * @cfg {Number} endDate default Infinity
22867  * @cfg {Boolean} todayHighlight default false
22868  * @cfg {Boolean} todayBtn default false
22869  * @cfg {Boolean} calendarWeeks default false
22870  * @cfg {Object} daysOfWeekDisabled default empty
22871  * @cfg {Boolean} singleMode default false (true | false)
22872  * 
22873  * @cfg {Boolean} keyboardNavigation default true
22874  * @cfg {String} language default en
22875  * 
22876  * @constructor
22877  * Create a new DateField
22878  * @param {Object} config The config object
22879  */
22880  
22881 Roo.bootstrap.form.DateField = function(config){
22882     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22883      this.addEvents({
22884             /**
22885              * @event show
22886              * Fires when this field show.
22887              * @param {Roo.bootstrap.form.DateField} this
22888              * @param {Mixed} date The date value
22889              */
22890             show : true,
22891             /**
22892              * @event show
22893              * Fires when this field hide.
22894              * @param {Roo.bootstrap.form.DateField} this
22895              * @param {Mixed} date The date value
22896              */
22897             hide : true,
22898             /**
22899              * @event select
22900              * Fires when select a date.
22901              * @param {Roo.bootstrap.form.DateField} this
22902              * @param {Mixed} date The date value
22903              */
22904             select : true,
22905             /**
22906              * @event beforeselect
22907              * Fires when before select a date.
22908              * @param {Roo.bootstrap.form.DateField} this
22909              * @param {Mixed} date The date value
22910              */
22911             beforeselect : true
22912         });
22913 };
22914
22915 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22916     
22917     /**
22918      * @cfg {String} format
22919      * The default date format string which can be overriden for localization support.  The format must be
22920      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22921      */
22922     format : "m/d/y",
22923     
22924     weekStart : 0,
22925     
22926     viewMode : '',
22927     
22928     minViewMode : '',
22929     
22930     todayHighlight : false,
22931     
22932     todayBtn: false,
22933     
22934     language: 'en',
22935     
22936     keyboardNavigation: true,
22937     
22938     calendarWeeks: false,
22939     
22940     startDate: -Infinity,
22941     
22942     endDate: Infinity,
22943     
22944     daysOfWeekDisabled: [],
22945     
22946     _events: [],
22947     
22948     singleMode : false,
22949
22950     hiddenField : false,
22951     
22952     UTCDate: function()
22953     {
22954         return new Date(Date.UTC.apply(Date, arguments));
22955     },
22956     
22957     UTCToday: function()
22958     {
22959         var today = new Date();
22960         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22961     },
22962     
22963     getDate: function() {
22964             var d = this.getUTCDate();
22965             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22966     },
22967     
22968     getUTCDate: function() {
22969             return this.date;
22970     },
22971     
22972     setDate: function(d) {
22973             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22974     },
22975     
22976     setUTCDate: function(d) {
22977             this.date = d;
22978             this.setValue(this.date);
22979     },
22980
22981     translateDates: function(lang) 
22982     {
22983         var translation = Roo.bootstrap.form.DateField.dates[lang] = {
22984             days: [],
22985             daysShort: [],
22986             daysMin: [],
22987             months: [],
22988             monthsShort: []
22989         };
22990
22991         var locale = lang.replace('_', '-');
22992
22993         var is_latin = [ 'zh-hk', 'zh-cn', 'jp', 'ko' ].indexOf(locale.toLowerCase()) < 0; 
22994                  
22995
22996         // fill days
22997         for(var i = 0; i < 7; i++) {
22998             var date = new Date(2020, 0, 5 + i);
22999
23000             var day = new Intl.DateTimeFormat(locale, {
23001                 weekday : 'long'
23002             }).format(date);
23003
23004             var dayShort = new Intl.DateTimeFormat(locale, {
23005                 weekday : 'short'
23006             }).format(date);
23007
23008             var dayMin = new Intl.DateTimeFormat(locale, {
23009                 weekday : 'narrow'
23010             }).format(date);
23011
23012             if(is_latin) {
23013                 dayShort = day.substring(0, 3);
23014                 dayMin = day.substring(0, 2);
23015             }
23016             
23017             translation.days.push(day);
23018             translation.daysShort.push(dayShort);
23019             translation.daysMin.push(dayMin);
23020         }
23021
23022         // fill months
23023         for(var i = 0; i < 12; i++) {
23024             var date = new Date(2020, i);
23025
23026             var month = new Intl.DateTimeFormat(locale, {
23027                 month : 'long'
23028             }).format(date);
23029
23030             var monthShort = new Intl.DateTimeFormat(locale, {
23031                 month : 'short'
23032             }).format(date);
23033
23034             if(is_latin) {
23035                 monthShort = month.substring(0, 3);
23036             }
23037
23038             translation.months.push(month);
23039             translation.monthsShort.push(monthShort);
23040         }
23041     },
23042         
23043     onRender: function(ct, position)
23044     {
23045         
23046         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
23047
23048         this.translateDates(this.language);
23049         
23050         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
23051         this.format = this.format || 'm/d/y';
23052         this.isInline = false;
23053         this.isInput = true;
23054         this.component = this.el.select('.add-on', true).first() || false;
23055         this.component = (this.component && this.component.length === 0) ? false : this.component;
23056         this.hasInput = this.component && this.inputEl().length;
23057         
23058         if (typeof(this.minViewMode === 'string')) {
23059             switch (this.minViewMode) {
23060                 case 'months':
23061                     this.minViewMode = 1;
23062                     break;
23063                 case 'years':
23064                     this.minViewMode = 2;
23065                     break;
23066                 default:
23067                     this.minViewMode = 0;
23068                     break;
23069             }
23070         }
23071         
23072         if (typeof(this.viewMode === 'string')) {
23073             switch (this.viewMode) {
23074                 case 'months':
23075                     this.viewMode = 1;
23076                     break;
23077                 case 'years':
23078                     this.viewMode = 2;
23079                     break;
23080                 default:
23081                     this.viewMode = 0;
23082                     break;
23083             }
23084         }
23085                 
23086         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23087         
23088 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23089         
23090         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23091         
23092         this.picker().on('mousedown', this.onMousedown, this);
23093         this.picker().on('click', this.onClick, this);
23094         
23095         this.picker().addClass('datepicker-dropdown');
23096         
23097         this.startViewMode = this.viewMode;
23098         
23099         if(this.singleMode){
23100             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23101                 v.setVisibilityMode(Roo.Element.DISPLAY);
23102                 v.hide();
23103             });
23104             
23105             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23106                 v.setStyle('width', '189px');
23107             });
23108         }
23109         
23110         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23111             v.dom.innerHTML = Roo.bootstrap.form.DateField.todayText;
23112         });
23113                         
23114         
23115         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23116         
23117         this.setStartDate(this.startDate);
23118         this.setEndDate(this.endDate);
23119         
23120         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23121         
23122         this.fillDow();
23123         this.fillMonths();
23124         this.update();
23125         this.showMode();
23126         
23127         if(this.isInline) {
23128             this.showPopup();
23129         }
23130
23131         this.hiddenField = this.inputEl().insertSibling(
23132             {tag : 'input', type : 'hidden', name : this.name},
23133             'before',
23134             true
23135         );
23136         this.inputEl().dom.setAttribute('name', this.name + '____hidden___');
23137
23138     },
23139     
23140     picker : function()
23141     {
23142         return this.pickerEl;
23143 //        return this.el.select('.datepicker', true).first();
23144     },
23145     
23146     fillDow: function()
23147     {
23148         var dowCnt = this.weekStart;
23149         
23150         var dow = {
23151             tag: 'tr',
23152             cn: [
23153                 
23154             ]
23155         };
23156         
23157         while (dowCnt < this.weekStart + 7) {
23158             dow.cn.push({
23159                 tag: 'th',
23160                 cls: 'dow',
23161                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23162             });
23163         }
23164         
23165         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23166     },
23167     
23168     fillMonths: function()
23169     {    
23170         var i = 0;
23171         var months = this.picker().select('>.datepicker-months td', true).first();
23172         
23173         months.dom.innerHTML = '';
23174         
23175         while (i < 12) {
23176             var month = {
23177                 tag: 'span',
23178                 cls: 'month',
23179                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23180             };
23181             
23182             months.createChild(month);
23183         }
23184         
23185     },
23186     
23187     update: function()
23188     {
23189         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;
23190         
23191         if (this.date < this.startDate) {
23192             this.viewDate = new Date(this.startDate);
23193         } else if (this.date > this.endDate) {
23194             this.viewDate = new Date(this.endDate);
23195         } else {
23196             this.viewDate = new Date(this.date);
23197         }
23198         
23199         this.fill();
23200     },
23201     
23202     fill: function() 
23203     {
23204         var d = new Date(this.viewDate),
23205                 year = d.getUTCFullYear(),
23206                 month = d.getUTCMonth(),
23207                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23208                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23209                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23210                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23211                 currentDate = this.date && this.date.valueOf(),
23212                 today = this.UTCToday();
23213         
23214         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23215     
23216         this.updateNavArrows();
23217         this.fillMonths();
23218                                                 
23219         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23220         
23221         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23222          
23223         prevMonth.setUTCDate(day);
23224         
23225         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23226         
23227         var nextMonth = new Date(prevMonth);
23228         
23229         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23230         
23231         nextMonth = nextMonth.valueOf();
23232         
23233         var fillMonths = false;
23234         
23235         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23236         
23237         while(prevMonth.valueOf() <= nextMonth) {
23238             var clsName = '';
23239             
23240             if (prevMonth.getUTCDay() === this.weekStart) {
23241                 if(fillMonths){
23242                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23243                 }
23244                     
23245                 fillMonths = {
23246                     tag: 'tr',
23247                     cn: []
23248                 };
23249             }
23250             
23251             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23252                 clsName += ' old';
23253             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23254                 clsName += ' new';
23255             }
23256             if (this.todayHighlight &&
23257                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23258                 prevMonth.getUTCMonth() == today.getMonth() &&
23259                 prevMonth.getUTCDate() == today.getDate()) {
23260                 clsName += ' today';
23261             }
23262             
23263             if (currentDate && prevMonth.valueOf() === currentDate) {
23264                 clsName += ' active';
23265             }
23266             
23267             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23268                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23269                     clsName += ' disabled';
23270             }
23271             
23272             fillMonths.cn.push({
23273                 tag: 'td',
23274                 cls: 'day ' + clsName,
23275                 html: prevMonth.getDate()
23276             });
23277             
23278             prevMonth.setDate(prevMonth.getDate()+1);
23279         }
23280           
23281         var currentYear = this.date && this.date.getUTCFullYear();
23282         var currentMonth = this.date && this.date.getUTCMonth();
23283         
23284         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23285         
23286         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23287             v.removeClass('active');
23288             
23289             if(currentYear === year && k === currentMonth){
23290                 v.addClass('active');
23291             }
23292             
23293             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23294                 v.addClass('disabled');
23295             }
23296             
23297         });
23298         
23299         
23300         year = parseInt(year/10, 10) * 10;
23301         
23302         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23303         
23304         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23305         
23306         year -= 1;
23307         for (var i = -1; i < 11; i++) {
23308             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23309                 tag: 'span',
23310                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23311                 html: year
23312             });
23313             
23314             year += 1;
23315         }
23316     },
23317     
23318     showMode: function(dir) 
23319     {
23320         if (dir) {
23321             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23322         }
23323         
23324         Roo.each(this.picker().select('>div',true).elements, function(v){
23325             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23326             v.hide();
23327         });
23328         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23329     },
23330     
23331     place: function()
23332     {
23333         if(this.isInline) {
23334             return;
23335         }
23336         
23337         this.picker().removeClass(['bottom', 'top']);
23338         
23339         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23340             /*
23341              * place to the top of element!
23342              *
23343              */
23344             
23345             this.picker().addClass('top');
23346             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23347             
23348             return;
23349         }
23350         
23351         this.picker().addClass('bottom');
23352         
23353         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23354     },
23355     
23356     // return false when it fails
23357     parseDate : function(value)
23358     {
23359         if(!value) {
23360             return false;
23361         }
23362         if(value instanceof Date){
23363             return value;
23364         }
23365         var v = Date.parseDate(value, 'Y-m-d');
23366
23367         return (typeof(v) == 'undefined') ? false : v;
23368     },
23369     
23370     formatDate : function(date, fmt)
23371     {   
23372         return (!date || !(date instanceof Date)) ?
23373         date : date.dateFormat(fmt || this.format);
23374     },
23375
23376     translateDate : function(date)
23377     {
23378         switch(this.language) {
23379             case 'zh_CN':
23380                 return new Intl.DateTimeFormat('zh-CN', {
23381                     year : 'numeric',
23382                     month : 'long',
23383                     day : 'numeric'
23384                 }).format(date);
23385             default :
23386                 return this.formatDate(date);
23387         }
23388     },
23389     
23390     onFocus : function()
23391     {
23392         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23393         this.showPopup();
23394     },
23395     
23396     onBlur : function()
23397     {
23398         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23399
23400         if(!this.readOnly) {
23401             var d = this.inputEl().getValue();
23402             var date = this.parseDate(d);
23403             if(date) {
23404                 this.setValue(date);
23405             }
23406             else {
23407                 this.setValue(this.getValue());
23408             }
23409         }
23410                 
23411         this.hidePopup();
23412     },
23413     
23414     showPopup : function()
23415     {
23416         this.picker().show();
23417         this.update();
23418         this.place();
23419         
23420         this.fireEvent('showpopup', this, this.date);
23421     },
23422     
23423     hidePopup : function()
23424     {
23425         if(this.isInline) {
23426             return;
23427         }
23428         this.picker().hide();
23429         this.viewMode = this.startViewMode;
23430         this.showMode();
23431         
23432         this.fireEvent('hidepopup', this, this.date);
23433         
23434     },
23435     
23436     onMousedown: function(e)
23437     {
23438         e.stopPropagation();
23439         e.preventDefault();
23440     },
23441     
23442     keyup: function(e)
23443     {
23444         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23445         this.update();
23446     },
23447
23448     setValue: function(v)
23449     {
23450         if(this.fireEvent('beforeselect', this, v) !== false){
23451             var d = this.parseDate(v);
23452
23453             if(!d) {
23454                 this.date = this.viewDate = this.value = this.hiddenField.value =  '';
23455                 if(this.rendered){
23456                     this.inputEl().dom.value = '';
23457                     this.validate();
23458                 }
23459                 return;
23460             }
23461
23462             d = new Date(d).clearTime();
23463
23464             this.value = this.hiddenField.value = d.dateFormat('Y-m-d');
23465
23466             v = this.translateDate(d);
23467             if(this.rendered){
23468                 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
23469                 this.validate();
23470             }
23471
23472             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23473
23474             this.update();
23475
23476             this.fireEvent('select', this, this.date);
23477         }
23478     },
23479
23480     // bypass validation
23481     setRawValue : function(v){
23482         if(this.fireEvent('beforeselect', this, v) !== false){
23483             var d = this.parseDate(v);
23484
23485             if(!d) {
23486                 this.date = this.viewDate = this.value = this.hiddenField.value =  '';
23487                 if(this.rendered){
23488                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
23489                 }
23490                 return;
23491             }
23492
23493             d = new Date(d).clearTime();
23494
23495             this.value = this.hiddenField.value = d.dateFormat('Y-m-d');
23496
23497             v = this.translateDate(d);
23498             if(this.rendered){
23499                 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
23500             }
23501
23502             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23503
23504             this.update();
23505
23506             this.fireEvent('select', this, this.date);
23507         }
23508     },
23509     
23510     getValue: function()
23511     {
23512         return this.value;
23513     },
23514
23515     getRawValue : function(){
23516         return this.getValue();
23517     },
23518     
23519     fireKey: function(e)
23520     {
23521         if (!this.picker().isVisible()){
23522             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23523                 this.showPopup();
23524             }
23525             return;
23526         }
23527         
23528         var dateChanged = false,
23529         dir, day, month,
23530         newDate, newViewDate;
23531         
23532         switch(e.keyCode){
23533             case 27: // escape
23534                 this.hidePopup();
23535                 e.preventDefault();
23536                 break;
23537             case 37: // left
23538             case 39: // right
23539                 if (!this.keyboardNavigation) {
23540                     break;
23541                 }
23542                 dir = e.keyCode == 37 ? -1 : 1;
23543                 
23544                 if (e.ctrlKey){
23545                     newDate = this.moveYear(this.date, dir);
23546                     newViewDate = this.moveYear(this.viewDate, dir);
23547                 } else if (e.shiftKey){
23548                     newDate = this.moveMonth(this.date, dir);
23549                     newViewDate = this.moveMonth(this.viewDate, dir);
23550                 } else {
23551                     newDate = new Date(this.date);
23552                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23553                     newViewDate = new Date(this.viewDate);
23554                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23555                 }
23556                 if (this.dateWithinRange(newDate)){
23557                     this.date = newDate;
23558                     this.viewDate = newViewDate;
23559                     this.setValue(this.date);
23560 //                    this.update();
23561                     e.preventDefault();
23562                     dateChanged = true;
23563                 }
23564                 break;
23565             case 38: // up
23566             case 40: // down
23567                 if (!this.keyboardNavigation) {
23568                     break;
23569                 }
23570                 dir = e.keyCode == 38 ? -1 : 1;
23571                 if (e.ctrlKey){
23572                     newDate = this.moveYear(this.date, dir);
23573                     newViewDate = this.moveYear(this.viewDate, dir);
23574                 } else if (e.shiftKey){
23575                     newDate = this.moveMonth(this.date, dir);
23576                     newViewDate = this.moveMonth(this.viewDate, dir);
23577                 } else {
23578                     newDate = new Date(this.date);
23579                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23580                     newViewDate = new Date(this.viewDate);
23581                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23582                 }
23583                 if (this.dateWithinRange(newDate)){
23584                     this.date = newDate;
23585                     this.viewDate = newViewDate;
23586                     this.setValue(this.date);
23587 //                    this.update();
23588                     e.preventDefault();
23589                     dateChanged = true;
23590                 }
23591                 break;
23592             case 13: // enter
23593                 this.setValue(this.date);
23594                 this.hidePopup();
23595                 e.preventDefault();
23596                 break;
23597             case 9: // tab
23598                 this.setValue(this.date);
23599                 this.hidePopup();
23600                 break;
23601             case 16: // shift
23602             case 17: // ctrl
23603             case 18: // alt
23604                 break;
23605             default :
23606                 this.hidePopup();
23607                 
23608         }
23609     },
23610     
23611     
23612     onClick: function(e) 
23613     {
23614         e.stopPropagation();
23615         e.preventDefault();
23616         
23617         var target = e.getTarget();
23618         
23619         if(target.nodeName.toLowerCase() === 'i'){
23620             target = Roo.get(target).dom.parentNode;
23621         }
23622         
23623         var nodeName = target.nodeName;
23624         var className = target.className;
23625         var html = target.innerHTML;
23626         //Roo.log(nodeName);
23627         
23628         switch(nodeName.toLowerCase()) {
23629             case 'th':
23630                 switch(className) {
23631                     case 'switch':
23632                         this.showMode(1);
23633                         break;
23634                     case 'prev':
23635                     case 'next':
23636                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23637                         switch(this.viewMode){
23638                                 case 0:
23639                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23640                                         break;
23641                                 case 1:
23642                                 case 2:
23643                                         this.viewDate = this.moveYear(this.viewDate, dir);
23644                                         break;
23645                         }
23646                         this.fill();
23647                         break;
23648                     case 'today':
23649                         var date = new Date();
23650                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23651 //                        this.fill()
23652                         this.setValue(this.date);
23653                         
23654                         this.hidePopup();
23655                         break;
23656                 }
23657                 break;
23658             case 'span':
23659                 if (className.indexOf('disabled') < 0) {
23660                 if (!this.viewDate) {
23661                     this.viewDate = new Date();
23662                 }
23663                 this.viewDate.setUTCDate(1);
23664                     if (className.indexOf('month') > -1) {
23665                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23666                     } else {
23667                         var year = parseInt(html, 10) || 0;
23668                         this.viewDate.setUTCFullYear(year);
23669                         
23670                     }
23671                     
23672                     if(this.singleMode){
23673                         this.setValue(this.viewDate);
23674                         this.hidePopup();
23675                         return;
23676                     }
23677                     
23678                     this.showMode(-1);
23679                     this.fill();
23680                 }
23681                 break;
23682                 
23683             case 'td':
23684                 //Roo.log(className);
23685                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23686                     var day = parseInt(html, 10) || 1;
23687                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23688                         month = (this.viewDate || new Date()).getUTCMonth();
23689
23690                     if (className.indexOf('old') > -1) {
23691                         if(month === 0 ){
23692                             month = 11;
23693                             year -= 1;
23694                         }else{
23695                             month -= 1;
23696                         }
23697                     } else if (className.indexOf('new') > -1) {
23698                         if (month == 11) {
23699                             month = 0;
23700                             year += 1;
23701                         } else {
23702                             month += 1;
23703                         }
23704                     }
23705                     //Roo.log([year,month,day]);
23706                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23707                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23708 //                    this.fill();
23709                     this.setValue(this.date);
23710                     this.hidePopup();
23711                 }
23712                 break;
23713         }
23714     },
23715     
23716     setStartDate: function(startDate)
23717     {
23718         this.startDate = startDate || -Infinity;
23719         if (this.startDate !== -Infinity) {
23720             var date = this.parseDate(this.startDate);
23721             this.startDate = date ? date : -Infinity;
23722         }
23723         this.update();
23724         this.updateNavArrows();
23725     },
23726
23727     setEndDate: function(endDate)
23728     {
23729         this.endDate = endDate || Infinity;
23730         if (this.endDate !== Infinity) {
23731             var date = this.parseDate(this.endDate);
23732             this.endDate = date ? date : Infinity;
23733         }
23734         this.update();
23735         this.updateNavArrows();
23736     },
23737     
23738     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23739     {
23740         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23741         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23742             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23743         }
23744         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23745             return parseInt(d, 10);
23746         });
23747         this.update();
23748         this.updateNavArrows();
23749     },
23750     
23751     updateNavArrows: function() 
23752     {
23753         if(this.singleMode){
23754             return;
23755         }
23756         
23757         var d = new Date(this.viewDate),
23758         year = d.getUTCFullYear(),
23759         month = d.getUTCMonth();
23760         
23761         Roo.each(this.picker().select('.prev', true).elements, function(v){
23762             v.show();
23763             switch (this.viewMode) {
23764                 case 0:
23765
23766                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23767                         v.hide();
23768                     }
23769                     break;
23770                 case 1:
23771                 case 2:
23772                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23773                         v.hide();
23774                     }
23775                     break;
23776             }
23777         });
23778         
23779         Roo.each(this.picker().select('.next', true).elements, function(v){
23780             v.show();
23781             switch (this.viewMode) {
23782                 case 0:
23783
23784                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23785                         v.hide();
23786                     }
23787                     break;
23788                 case 1:
23789                 case 2:
23790                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23791                         v.hide();
23792                     }
23793                     break;
23794             }
23795         })
23796     },
23797     
23798     moveMonth: function(date, dir)
23799     {
23800         if (!dir) {
23801             return date;
23802         }
23803         var new_date = new Date(date.valueOf()),
23804         day = new_date.getUTCDate(),
23805         month = new_date.getUTCMonth(),
23806         mag = Math.abs(dir),
23807         new_month, test;
23808         dir = dir > 0 ? 1 : -1;
23809         if (mag == 1){
23810             test = dir == -1
23811             // If going back one month, make sure month is not current month
23812             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23813             ? function(){
23814                 return new_date.getUTCMonth() == month;
23815             }
23816             // If going forward one month, make sure month is as expected
23817             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23818             : function(){
23819                 return new_date.getUTCMonth() != new_month;
23820             };
23821             new_month = month + dir;
23822             new_date.setUTCMonth(new_month);
23823             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23824             if (new_month < 0 || new_month > 11) {
23825                 new_month = (new_month + 12) % 12;
23826             }
23827         } else {
23828             // For magnitudes >1, move one month at a time...
23829             for (var i=0; i<mag; i++) {
23830                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23831                 new_date = this.moveMonth(new_date, dir);
23832             }
23833             // ...then reset the day, keeping it in the new month
23834             new_month = new_date.getUTCMonth();
23835             new_date.setUTCDate(day);
23836             test = function(){
23837                 return new_month != new_date.getUTCMonth();
23838             };
23839         }
23840         // Common date-resetting loop -- if date is beyond end of month, make it
23841         // end of month
23842         while (test()){
23843             new_date.setUTCDate(--day);
23844             new_date.setUTCMonth(new_month);
23845         }
23846         return new_date;
23847     },
23848
23849     moveYear: function(date, dir)
23850     {
23851         return this.moveMonth(date, dir*12);
23852     },
23853
23854     dateWithinRange: function(date)
23855     {
23856         return date >= this.startDate && date <= this.endDate;
23857     },
23858
23859     
23860     remove: function() 
23861     {
23862         this.picker().remove();
23863     },
23864     
23865     validateValue : function(value)
23866     {
23867         if(this.getVisibilityEl().hasClass('hidden')){
23868             return true;
23869         }
23870         
23871         if(value.length < 1)  {
23872             if(this.allowBlank){
23873                 return true;
23874             }
23875             return false;
23876         }
23877         
23878         if(value.length < this.minLength){
23879             return false;
23880         }
23881         if(value.length > this.maxLength){
23882             return false;
23883         }
23884         if(this.vtype){
23885             var vt = Roo.form.VTypes;
23886             if(!vt[this.vtype](value, this)){
23887                 return false;
23888             }
23889         }
23890         if(typeof this.validator == "function"){
23891             var msg = this.validator(value);
23892             if(msg !== true){
23893                 return false;
23894             }
23895         }
23896         
23897         if(this.regex && !this.regex.test(value)){
23898             return false;
23899         }
23900         
23901         if(!this.parseDate(value)){
23902             return false;
23903         }
23904         
23905         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23906             return false;
23907         }      
23908         
23909         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23910             return false;
23911         } 
23912         
23913         
23914         return true;
23915     },
23916     
23917     reset : function()
23918     {
23919         this.date = this.viewDate = '';
23920         
23921         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23922     }
23923    
23924 });
23925
23926 Roo.apply(Roo.bootstrap.form.DateField,  {
23927     
23928     head : {
23929         tag: 'thead',
23930         cn: [
23931         {
23932             tag: 'tr',
23933             cn: [
23934             {
23935                 tag: 'th',
23936                 cls: 'prev',
23937                 html: '<i class="fa fa-arrow-left"/>'
23938             },
23939             {
23940                 tag: 'th',
23941                 cls: 'switch',
23942                 colspan: '5'
23943             },
23944             {
23945                 tag: 'th',
23946                 cls: 'next',
23947                 html: '<i class="fa fa-arrow-right"/>'
23948             }
23949
23950             ]
23951         }
23952         ]
23953     },
23954     
23955     content : {
23956         tag: 'tbody',
23957         cn: [
23958         {
23959             tag: 'tr',
23960             cn: [
23961             {
23962                 tag: 'td',
23963                 colspan: '7'
23964             }
23965             ]
23966         }
23967         ]
23968     },
23969     
23970     footer : {
23971         tag: 'tfoot',
23972         cn: [
23973         {
23974             tag: 'tr',
23975             cn: [
23976             {
23977                 tag: 'th',
23978                 colspan: '7',
23979                 cls: 'today'
23980             }
23981                     
23982             ]
23983         }
23984         ]
23985     },
23986     
23987     dates : {},
23988
23989     todayText : "Today",
23990     
23991     modes: [
23992     {
23993         clsName: 'days',
23994         navFnc: 'Month',
23995         navStep: 1
23996     },
23997     {
23998         clsName: 'months',
23999         navFnc: 'FullYear',
24000         navStep: 1
24001     },
24002     {
24003         clsName: 'years',
24004         navFnc: 'FullYear',
24005         navStep: 10
24006     }]
24007 });
24008
24009 Roo.apply(Roo.bootstrap.form.DateField,  {
24010   
24011     template : {
24012         tag: 'div',
24013         cls: 'datepicker dropdown-menu roo-dynamic shadow',
24014         cn: [
24015         {
24016             tag: 'div',
24017             cls: 'datepicker-days',
24018             cn: [
24019             {
24020                 tag: 'table',
24021                 cls: 'table-condensed',
24022                 cn:[
24023                 Roo.bootstrap.form.DateField.head,
24024                 {
24025                     tag: 'tbody'
24026                 },
24027                 Roo.bootstrap.form.DateField.footer
24028                 ]
24029             }
24030             ]
24031         },
24032         {
24033             tag: 'div',
24034             cls: 'datepicker-months',
24035             cn: [
24036             {
24037                 tag: 'table',
24038                 cls: 'table-condensed',
24039                 cn:[
24040                 Roo.bootstrap.form.DateField.head,
24041                 Roo.bootstrap.form.DateField.content,
24042                 Roo.bootstrap.form.DateField.footer
24043                 ]
24044             }
24045             ]
24046         },
24047         {
24048             tag: 'div',
24049             cls: 'datepicker-years',
24050             cn: [
24051             {
24052                 tag: 'table',
24053                 cls: 'table-condensed',
24054                 cn:[
24055                 Roo.bootstrap.form.DateField.head,
24056                 Roo.bootstrap.form.DateField.content,
24057                 Roo.bootstrap.form.DateField.footer
24058                 ]
24059             }
24060             ]
24061         }
24062         ]
24063     }
24064 });
24065
24066  
24067
24068  /*
24069  * - LGPL
24070  *
24071  * TimeField
24072  * 
24073  */
24074
24075 /**
24076  * @class Roo.bootstrap.form.TimeField
24077  * @extends Roo.bootstrap.form.Input
24078  * Bootstrap DateField class
24079  * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
24080  * 
24081  * 
24082  * @constructor
24083  * Create a new TimeField
24084  * @param {Object} config The config object
24085  */
24086
24087 Roo.bootstrap.form.TimeField = function(config){
24088     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
24089     this.addEvents({
24090             /**
24091              * @event show
24092              * Fires when this field show.
24093              * @param {Roo.bootstrap.form.DateField} thisthis
24094              * @param {Mixed} date The date value
24095              */
24096             show : true,
24097             /**
24098              * @event show
24099              * Fires when this field hide.
24100              * @param {Roo.bootstrap.form.DateField} this
24101              * @param {Mixed} date The date value
24102              */
24103             hide : true,
24104             /**
24105              * @event select
24106              * Fires when select a date.
24107              * @param {Roo.bootstrap.form.DateField} this
24108              * @param {Mixed} date The date value
24109              */
24110             select : true
24111         });
24112 };
24113
24114 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
24115     
24116     /**
24117      * @cfg {String} format
24118      * The default time format string which can be overriden for localization support.  The format must be
24119      * valid according to {@link Date#parseDate} (defaults to 'H:i').
24120      */
24121     format : "H:i",
24122     minuteStep : 1,
24123     language : 'en',
24124     hiddenField : false,
24125     getAutoCreate : function()
24126     {
24127         this.after = '<i class="fa far fa-clock"></i>';
24128         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24129         
24130          
24131     },
24132     onRender: function(ct, position)
24133     {
24134         
24135         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24136
24137         this.language = this.language in Roo.bootstrap.form.TimeField.periodText ? this.language : "en";
24138                 
24139         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24140         
24141         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24142         
24143         this.pop = this.picker().select('>.datepicker-time',true).first();
24144         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24145         
24146         this.picker().on('mousedown', this.onMousedown, this);
24147         this.picker().on('click', this.onClick, this);
24148         
24149         this.picker().addClass('datepicker-dropdown');
24150     
24151         this.fillTime();
24152         this.update();
24153             
24154         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24155         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24156         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24157         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24158         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24159         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24160         this.pop.select('button.ok', true).first().dom.innerHTML = Roo.bootstrap.form.TimeField.okText;
24161
24162         this.hiddenField = this.inputEl().insertSibling(
24163             {tag : 'input', type : 'hidden', name : this.name},
24164             'before',
24165             true
24166         );
24167         this.inputEl().dom.setAttribute('name', this.name + '____hidden___');
24168
24169     },
24170     
24171     fireKey: function(e){
24172         if (!this.picker().isVisible()){
24173             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24174                 this.show();
24175             }
24176             return;
24177         }
24178
24179         e.preventDefault();
24180         
24181         switch(e.keyCode){
24182             case 27: // escape
24183                 this.hide();
24184                 break;
24185             case 37: // left
24186             case 39: // right
24187                 this.onTogglePeriod();
24188                 break;
24189             case 38: // up
24190                 this.onIncrementMinutes();
24191                 break;
24192             case 40: // down
24193                 this.onDecrementMinutes();
24194                 break;
24195             case 13: // enter
24196             case 9: // tab
24197                 this.setTime();
24198                 break;
24199         }
24200     },
24201     
24202     onClick: function(e) {
24203         e.stopPropagation();
24204         e.preventDefault();
24205     },
24206     
24207     picker : function()
24208     {
24209         return this.pickerEl;
24210     },
24211     
24212     fillTime: function()
24213     {    
24214         var time = this.pop.select('tbody', true).first();
24215         
24216         time.dom.innerHTML = '';
24217         
24218         time.createChild({
24219             tag: 'tr',
24220             cn: [
24221                 {
24222                     tag: 'td',
24223                     cn: [
24224                         {
24225                             tag: 'a',
24226                             href: '#',
24227                             cls: 'btn',
24228                             cn: [
24229                                 {
24230                                     tag: 'i',
24231                                     cls: 'hours-up fa fas fa-chevron-up'
24232                                 }
24233                             ]
24234                         } 
24235                     ]
24236                 },
24237                 {
24238                     tag: 'td',
24239                     cls: 'separator'
24240                 },
24241                 {
24242                     tag: 'td',
24243                     cn: [
24244                         {
24245                             tag: 'a',
24246                             href: '#',
24247                             cls: 'btn',
24248                             cn: [
24249                                 {
24250                                     tag: 'i',
24251                                     cls: 'minutes-up fa fas fa-chevron-up'
24252                                 }
24253                             ]
24254                         }
24255                     ]
24256                 },
24257                 {
24258                     tag: 'td',
24259                     cls: 'separator'
24260                 }
24261             ]
24262         });
24263         
24264         time.createChild({
24265             tag: 'tr',
24266             cn: [
24267                 {
24268                     tag: 'td',
24269                     cn: [
24270                         {
24271                             tag: 'span',
24272                             cls: 'timepicker-hour',
24273                             html: '00'
24274                         }  
24275                     ]
24276                 },
24277                 {
24278                     tag: 'td',
24279                     cls: 'separator',
24280                     html: ':'
24281                 },
24282                 {
24283                     tag: 'td',
24284                     cn: [
24285                         {
24286                             tag: 'span',
24287                             cls: 'timepicker-minute',
24288                             html: '00'
24289                         }  
24290                     ]
24291                 },
24292                 {
24293                     tag: 'td',
24294                     cls: 'separator'
24295                 },
24296                 {
24297                     tag: 'td',
24298                     cn: [
24299                         {
24300                             tag: 'button',
24301                             type: 'button',
24302                             cls: 'btn btn-primary period',
24303                             html: 'AM'
24304                             
24305                         }
24306                     ]
24307                 }
24308             ]
24309         });
24310         
24311         time.createChild({
24312             tag: 'tr',
24313             cn: [
24314                 {
24315                     tag: 'td',
24316                     cn: [
24317                         {
24318                             tag: 'a',
24319                             href: '#',
24320                             cls: 'btn',
24321                             cn: [
24322                                 {
24323                                     tag: 'span',
24324                                     cls: 'hours-down fa fas fa-chevron-down'
24325                                 }
24326                             ]
24327                         }
24328                     ]
24329                 },
24330                 {
24331                     tag: 'td',
24332                     cls: 'separator'
24333                 },
24334                 {
24335                     tag: 'td',
24336                     cn: [
24337                         {
24338                             tag: 'a',
24339                             href: '#',
24340                             cls: 'btn',
24341                             cn: [
24342                                 {
24343                                     tag: 'span',
24344                                     cls: 'minutes-down fa fas fa-chevron-down'
24345                                 }
24346                             ]
24347                         }
24348                     ]
24349                 },
24350                 {
24351                     tag: 'td',
24352                     cls: 'separator'
24353                 }
24354             ]
24355         });
24356         
24357     },
24358     
24359     update: function()
24360     {
24361         // default minute is a multiple of minuteStep
24362         if(typeof(this.time) === 'undefined' || this.time.length == 0) {
24363             this.time = new Date();
24364             this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24365         }
24366         this.time = (typeof(this.time) === 'undefined' || this.time.length == 0) ? new Date() : this.time;
24367         
24368         this.fill();
24369     },
24370     
24371     fill: function() 
24372     {
24373         var hours = this.time.getHours();
24374         var minutes = this.time.getMinutes();
24375         var period = Roo.bootstrap.form.TimeField.periodText[this.language]['am'];
24376         
24377         if(hours > 11){
24378             period = Roo.bootstrap.form.TimeField.periodText[this.language]['pm'];
24379         }
24380         
24381         if(hours == 0){
24382             hours = 12;
24383         }
24384         
24385         
24386         if(hours > 12){
24387             hours = hours - 12;
24388         }
24389         
24390         if(hours < 10){
24391             hours = '0' + hours;
24392         }
24393         
24394         if(minutes < 10){
24395             minutes = '0' + minutes;
24396         }
24397         
24398         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24399         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24400         this.pop.select('button', true).first().dom.innerHTML = period;
24401         
24402     },
24403     
24404     place: function()
24405     {   
24406         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24407         
24408         var cls = ['bottom'];
24409         
24410         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24411             cls.pop();
24412             cls.push('top');
24413         }
24414         
24415         cls.push('right');
24416         
24417         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24418             cls.pop();
24419             cls.push('left');
24420         }
24421         //this.picker().setXY(20000,20000);
24422         this.picker().addClass(cls.join('-'));
24423         
24424         var _this = this;
24425         
24426         Roo.each(cls, function(c){
24427             if(c == 'bottom'){
24428                 (function() {
24429                  //  
24430                 }).defer(200);
24431                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24432                 //_this.picker().setTop(_this.inputEl().getHeight());
24433                 return;
24434             }
24435             if(c == 'top'){
24436                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24437                 
24438                 //_this.picker().setTop(0 - _this.picker().getHeight());
24439                 return;
24440             }
24441             /*
24442             if(c == 'left'){
24443                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24444                 return;
24445             }
24446             if(c == 'right'){
24447                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24448                 return;
24449             }
24450             */
24451         });
24452         
24453     },
24454   
24455     onFocus : function()
24456     {
24457         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24458         this.show();
24459     },
24460     
24461     onBlur : function()
24462     {
24463         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24464         this.hide();
24465     },
24466     
24467     show : function()
24468     {
24469         this.picker().show();
24470         this.pop.show();
24471         this.update();
24472         this.place();
24473         
24474         this.fireEvent('show', this, this.time);
24475     },
24476     
24477     hide : function()
24478     {
24479         this.picker().hide();
24480         this.pop.hide();
24481         
24482         this.fireEvent('hide', this, this.time);
24483     },
24484     
24485     setTime : function()
24486     {
24487         this.hide();
24488         this.setValue(this.time);
24489         
24490         this.fireEvent('select', this, this.time);
24491         
24492         
24493     },
24494
24495     // return false when it fails
24496     parseTime : function(value)
24497     {
24498         if(!value) {
24499             return false;
24500         }
24501         if(value instanceof Date){
24502             return value;
24503         }
24504         var v = Date.parseDate(value, 'H:i:s');
24505
24506         return (typeof(v) == 'undefined') ? false : v;
24507     },
24508
24509     translateTime : function(time)
24510     {
24511         switch(this.language) {
24512             case 'zh_CN':
24513                 return new Intl.DateTimeFormat('zh-CN', {
24514                     hour : 'numeric',
24515                     minute : 'numeric',
24516                     hour12 : true
24517                 }).format(time);
24518             default :
24519                 return time.format(this.format);
24520         }
24521     },
24522
24523     setValue: function(v)
24524     {
24525         var t = this.parseTime(v);
24526
24527         if(!t) {
24528             this.time = this.value = this.hiddenField.value =  '';
24529             if(this.rendered){
24530                 this.inputEl().dom.value = '';
24531                 this.validate();
24532             }
24533             return;
24534         }
24535
24536         this.value = this.hiddenField.value = t.dateFormat('H:i:s');
24537
24538         v = this.translateTime(t);
24539
24540         if(this.rendered){
24541             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
24542             this.validate();
24543         }
24544
24545         this.time = t;
24546
24547         this.update();
24548     },
24549
24550     setRawValue: function(v)
24551     {
24552         var t = this.parseTime(v);
24553
24554         if(!t) {
24555             this.time = this.value = this.hiddenField.value =  '';
24556             if(this.rendered){
24557                 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
24558             }
24559             return;
24560         }
24561
24562         this.value = this.hiddenField.value = t.dateFormat('H:i:s');
24563
24564         v = this.translateTime(t);
24565
24566         if(this.rendered){
24567             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
24568         }
24569
24570         this.time = t;
24571
24572         this.update();
24573     },
24574
24575     getValue: function()
24576     {
24577         return this.value;
24578     },
24579
24580     getRawValue : function(){
24581         return this.getValue();
24582     },
24583     
24584     onMousedown: function(e){
24585         e.stopPropagation();
24586         e.preventDefault();
24587     },
24588     
24589     onIncrementHours: function()
24590     {
24591         Roo.log('onIncrementHours');
24592         this.time = this.time.add(Date.HOUR, 1);
24593         this.update();
24594         
24595     },
24596     
24597     onDecrementHours: function()
24598     {
24599         Roo.log('onDecrementHours');
24600         this.time = this.time.add(Date.HOUR, -1);
24601         this.update();
24602     },
24603     
24604     onIncrementMinutes: function()
24605     {
24606         Roo.log('onIncrementMinutes');
24607         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24608         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24609         this.update();
24610     },
24611     
24612     onDecrementMinutes: function()
24613     {
24614         Roo.log('onDecrementMinutes');
24615         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24616         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24617         this.update();
24618     },
24619     
24620     onTogglePeriod: function()
24621     {
24622         Roo.log('onTogglePeriod');
24623         this.time = this.time.add(Date.HOUR, 12);
24624         this.update();
24625     }
24626     
24627    
24628 });
24629 Roo.apply(Roo.bootstrap.form.TimeField,  {
24630     okText : 'OK',
24631     periodText : {
24632         en : {
24633             am : 'AM',
24634             pm : 'PM'
24635         },
24636         zh_CN : {
24637             am : '上午',
24638             pm : '下午'
24639         }
24640     }
24641 });
24642
24643 Roo.apply(Roo.bootstrap.form.TimeField,  {
24644     template : {
24645         tag: 'div',
24646         cls: 'datepicker dropdown-menu',
24647         cn: [
24648             {
24649                 tag: 'div',
24650                 cls: 'datepicker-time',
24651                 cn: [
24652                 {
24653                     tag: 'table',
24654                     cls: 'table-condensed',
24655                     cn:[
24656                         {
24657                             tag: 'tbody',
24658                             cn: [
24659                                 {
24660                                     tag: 'tr',
24661                                     cn: [
24662                                     {
24663                                         tag: 'td',
24664                                         colspan: '7'
24665                                     }
24666                                     ]
24667                                 }
24668                             ]
24669                         },
24670                         {
24671                             tag: 'tfoot',
24672                             cn: [
24673                                 {
24674                                     tag: 'tr',
24675                                     cn: [
24676                                     {
24677                                         tag: 'th',
24678                                         colspan: '7',
24679                                         cls: '',
24680                                         cn: [
24681                                             {
24682                                                 tag: 'button',
24683                                                 cls: 'btn btn-info ok',
24684                                                 html: "OK" // this is overridden on construciton
24685                                             }
24686                                         ]
24687                                     }
24688                     
24689                                     ]
24690                                 }
24691                             ]
24692                         }
24693                     ]
24694                 }
24695                 ]
24696             }
24697         ]
24698     }
24699 });
24700
24701  
24702
24703  /*
24704  * - LGPL
24705  *
24706  * MonthField
24707  * 
24708  */
24709
24710 /**
24711  * @class Roo.bootstrap.form.MonthField
24712  * @extends Roo.bootstrap.form.Input
24713  * Bootstrap MonthField class
24714  * 
24715  * @cfg {String} language default en
24716  * 
24717  * @constructor
24718  * Create a new MonthField
24719  * @param {Object} config The config object
24720  */
24721
24722 Roo.bootstrap.form.MonthField = function(config){
24723     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24724     
24725     this.addEvents({
24726         /**
24727          * @event show
24728          * Fires when this field show.
24729          * @param {Roo.bootstrap.form.MonthField} this
24730          * @param {Mixed} date The date value
24731          */
24732         show : true,
24733         /**
24734          * @event show
24735          * Fires when this field hide.
24736          * @param {Roo.bootstrap.form.MonthField} this
24737          * @param {Mixed} date The date value
24738          */
24739         hide : true,
24740         /**
24741          * @event select
24742          * Fires when select a date.
24743          * @param {Roo.bootstrap.form.MonthField} this
24744          * @param {String} oldvalue The old value
24745          * @param {String} newvalue The new value
24746          */
24747         select : true
24748     });
24749 };
24750
24751 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24752     
24753     onRender: function(ct, position)
24754     {
24755         
24756         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24757         
24758         this.language = this.language || 'en';
24759         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24760         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24761         
24762         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24763         this.isInline = false;
24764         this.isInput = true;
24765         this.component = this.el.select('.add-on', true).first() || false;
24766         this.component = (this.component && this.component.length === 0) ? false : this.component;
24767         this.hasInput = this.component && this.inputEL().length;
24768         
24769         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24770         
24771         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24772         
24773         this.picker().on('mousedown', this.onMousedown, this);
24774         this.picker().on('click', this.onClick, this);
24775         
24776         this.picker().addClass('datepicker-dropdown');
24777         
24778         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24779             v.setStyle('width', '189px');
24780         });
24781         
24782         this.fillMonths();
24783         
24784         this.update();
24785         
24786         if(this.isInline) {
24787             this.show();
24788         }
24789         
24790     },
24791     
24792     setValue: function(v, suppressEvent)
24793     {   
24794         var o = this.getValue();
24795         
24796         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24797         
24798         this.update();
24799
24800         if(suppressEvent !== true){
24801             this.fireEvent('select', this, o, v);
24802         }
24803         
24804     },
24805     
24806     getValue: function()
24807     {
24808         return this.value;
24809     },
24810     
24811     onClick: function(e) 
24812     {
24813         e.stopPropagation();
24814         e.preventDefault();
24815         
24816         var target = e.getTarget();
24817         
24818         if(target.nodeName.toLowerCase() === 'i'){
24819             target = Roo.get(target).dom.parentNode;
24820         }
24821         
24822         var nodeName = target.nodeName;
24823         var className = target.className;
24824         var html = target.innerHTML;
24825         
24826         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24827             return;
24828         }
24829         
24830         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24831         
24832         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24833         
24834         this.hide();
24835                         
24836     },
24837     
24838     picker : function()
24839     {
24840         return this.pickerEl;
24841     },
24842     
24843     fillMonths: function()
24844     {    
24845         var i = 0;
24846         var months = this.picker().select('>.datepicker-months td', true).first();
24847         
24848         months.dom.innerHTML = '';
24849         
24850         while (i < 12) {
24851             var month = {
24852                 tag: 'span',
24853                 cls: 'month',
24854                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24855             };
24856             
24857             months.createChild(month);
24858         }
24859         
24860     },
24861     
24862     update: function()
24863     {
24864         var _this = this;
24865         
24866         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24867             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24868         }
24869         
24870         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24871             e.removeClass('active');
24872             
24873             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24874                 e.addClass('active');
24875             }
24876         })
24877     },
24878     
24879     place: function()
24880     {
24881         if(this.isInline) {
24882             return;
24883         }
24884         
24885         this.picker().removeClass(['bottom', 'top']);
24886         
24887         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24888             /*
24889              * place to the top of element!
24890              *
24891              */
24892             
24893             this.picker().addClass('top');
24894             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24895             
24896             return;
24897         }
24898         
24899         this.picker().addClass('bottom');
24900         
24901         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24902     },
24903     
24904     onFocus : function()
24905     {
24906         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24907         this.show();
24908     },
24909     
24910     onBlur : function()
24911     {
24912         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24913         
24914         var d = this.inputEl().getValue();
24915         
24916         this.setValue(d);
24917                 
24918         this.hide();
24919     },
24920     
24921     show : function()
24922     {
24923         this.picker().show();
24924         this.picker().select('>.datepicker-months', true).first().show();
24925         this.update();
24926         this.place();
24927         
24928         this.fireEvent('show', this, this.date);
24929     },
24930     
24931     hide : function()
24932     {
24933         if(this.isInline) {
24934             return;
24935         }
24936         this.picker().hide();
24937         this.fireEvent('hide', this, this.date);
24938         
24939     },
24940     
24941     onMousedown: function(e)
24942     {
24943         e.stopPropagation();
24944         e.preventDefault();
24945     },
24946     
24947     keyup: function(e)
24948     {
24949         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24950         this.update();
24951     },
24952
24953     fireKey: function(e)
24954     {
24955         if (!this.picker().isVisible()){
24956             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24957                 this.show();
24958             }
24959             return;
24960         }
24961         
24962         var dir;
24963         
24964         switch(e.keyCode){
24965             case 27: // escape
24966                 this.hide();
24967                 e.preventDefault();
24968                 break;
24969             case 37: // left
24970             case 39: // right
24971                 dir = e.keyCode == 37 ? -1 : 1;
24972                 
24973                 this.vIndex = this.vIndex + dir;
24974                 
24975                 if(this.vIndex < 0){
24976                     this.vIndex = 0;
24977                 }
24978                 
24979                 if(this.vIndex > 11){
24980                     this.vIndex = 11;
24981                 }
24982                 
24983                 if(isNaN(this.vIndex)){
24984                     this.vIndex = 0;
24985                 }
24986                 
24987                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24988                 
24989                 break;
24990             case 38: // up
24991             case 40: // down
24992                 
24993                 dir = e.keyCode == 38 ? -1 : 1;
24994                 
24995                 this.vIndex = this.vIndex + dir * 4;
24996                 
24997                 if(this.vIndex < 0){
24998                     this.vIndex = 0;
24999                 }
25000                 
25001                 if(this.vIndex > 11){
25002                     this.vIndex = 11;
25003                 }
25004                 
25005                 if(isNaN(this.vIndex)){
25006                     this.vIndex = 0;
25007                 }
25008                 
25009                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
25010                 break;
25011                 
25012             case 13: // enter
25013                 
25014                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
25015                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
25016                 }
25017                 
25018                 this.hide();
25019                 e.preventDefault();
25020                 break;
25021             case 9: // tab
25022                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
25023                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
25024                 }
25025                 this.hide();
25026                 break;
25027             case 16: // shift
25028             case 17: // ctrl
25029             case 18: // alt
25030                 break;
25031             default :
25032                 this.hide();
25033                 
25034         }
25035     },
25036     
25037     remove: function() 
25038     {
25039         this.picker().remove();
25040     }
25041    
25042 });
25043
25044 Roo.apply(Roo.bootstrap.form.MonthField,  {
25045     
25046     content : {
25047         tag: 'tbody',
25048         cn: [
25049         {
25050             tag: 'tr',
25051             cn: [
25052             {
25053                 tag: 'td',
25054                 colspan: '7'
25055             }
25056             ]
25057         }
25058         ]
25059     },
25060     
25061     dates:{
25062         en: {
25063             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
25064             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
25065         }
25066     }
25067 });
25068
25069 Roo.apply(Roo.bootstrap.form.MonthField,  {
25070   
25071     template : {
25072         tag: 'div',
25073         cls: 'datepicker dropdown-menu roo-dynamic',
25074         cn: [
25075             {
25076                 tag: 'div',
25077                 cls: 'datepicker-months',
25078                 cn: [
25079                 {
25080                     tag: 'table',
25081                     cls: 'table-condensed',
25082                     cn:[
25083                         Roo.bootstrap.form.DateField.content
25084                     ]
25085                 }
25086                 ]
25087             }
25088         ]
25089     }
25090 });
25091
25092  
25093
25094  
25095  /*
25096  * - LGPL
25097  *
25098  * CheckBox
25099  * 
25100  */
25101
25102 /**
25103  * @class Roo.bootstrap.form.CheckBox
25104  * @extends Roo.bootstrap.form.Input
25105  * Bootstrap CheckBox class
25106  * 
25107  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
25108  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
25109  * @cfg {String} boxLabel The text that appears beside the checkbox
25110  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
25111  * @cfg {Boolean} checked initnal the element
25112  * @cfg {Boolean} inline inline the element (default false)
25113  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
25114  * @cfg {String} tooltip label tooltip
25115  * 
25116  * @constructor
25117  * Create a new CheckBox
25118  * @param {Object} config The config object
25119  */
25120
25121 Roo.bootstrap.form.CheckBox = function(config){
25122     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
25123    
25124     this.addEvents({
25125         /**
25126         * @event check
25127         * Fires when the element is checked or unchecked.
25128         * @param {Roo.bootstrap.form.CheckBox} this This input
25129         * @param {Boolean} checked The new checked value
25130         */
25131        check : true,
25132        /**
25133         * @event click
25134         * Fires when the element is click.
25135         * @param {Roo.bootstrap.form.CheckBox} this This input
25136         */
25137        click : true
25138     });
25139     
25140 };
25141
25142 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
25143   
25144     inputType: 'checkbox',
25145     inputValue: 1,
25146     valueOff: 0,
25147     boxLabel: false,
25148     checked: false,
25149     weight : false,
25150     inline: false,
25151     tooltip : '',
25152     
25153     // checkbox success does not make any sense really.. 
25154     invalidClass : "",
25155     validClass : "",
25156     
25157     
25158     getAutoCreate : function()
25159     {
25160         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
25161         
25162         var id = Roo.id();
25163         
25164         var cfg = {};
25165         
25166         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
25167         
25168         if(this.inline){
25169             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
25170         }
25171         
25172         var input =  {
25173             tag: 'input',
25174             id : id,
25175             type : this.inputType,
25176             value : this.inputValue,
25177             cls : 'roo-' + this.inputType, //'form-box',
25178             placeholder : this.placeholder || ''
25179             
25180         };
25181         
25182         if(this.inputType != 'radio'){
25183             var hidden =  {
25184                 tag: 'input',
25185                 type : 'hidden',
25186                 cls : 'roo-hidden-value',
25187                 value : this.checked ? this.inputValue : this.valueOff
25188             };
25189         }
25190         
25191             
25192         if (this.weight) { // Validity check?
25193             cfg.cls += " " + this.inputType + "-" + this.weight;
25194         }
25195         
25196         if (this.disabled) {
25197             input.disabled=true;
25198         }
25199         
25200         if(this.checked){
25201             input.checked = this.checked;
25202         }
25203         
25204         if (this.name) {
25205             
25206             input.name = this.name;
25207             
25208             if(this.inputType != 'radio'){
25209                 hidden.name = this.name;
25210                 input.name = '_hidden_' + this.name;
25211             }
25212         }
25213         
25214         if (this.size) {
25215             input.cls += ' input-' + this.size;
25216         }
25217         
25218         var settings=this;
25219         
25220         ['xs','sm','md','lg'].map(function(size){
25221             if (settings[size]) {
25222                 cfg.cls += ' col-' + size + '-' + settings[size];
25223             }
25224         });
25225         
25226         var inputblock = input;
25227          
25228         if (this.before || this.after) {
25229             
25230             inputblock = {
25231                 cls : 'input-group',
25232                 cn :  [] 
25233             };
25234             
25235             if (this.before) {
25236                 inputblock.cn.push({
25237                     tag :'span',
25238                     cls : 'input-group-addon',
25239                     html : this.before
25240                 });
25241             }
25242             
25243             inputblock.cn.push(input);
25244             
25245             if(this.inputType != 'radio'){
25246                 inputblock.cn.push(hidden);
25247             }
25248             
25249             if (this.after) {
25250                 inputblock.cn.push({
25251                     tag :'span',
25252                     cls : 'input-group-addon',
25253                     html : this.after
25254                 });
25255             }
25256             
25257         }
25258         var boxLabelCfg = false;
25259         
25260         if(this.boxLabel){
25261            
25262             boxLabelCfg = {
25263                 tag: 'label',
25264                 //'for': id, // box label is handled by onclick - so no for...
25265                 cls: 'box-label',
25266                 html: this.boxLabel
25267             };
25268             if(this.tooltip){
25269                 boxLabelCfg.tooltip = this.tooltip;
25270             }
25271              
25272         }
25273         
25274         
25275         if (align ==='left' && this.fieldLabel.length) {
25276 //                Roo.log("left and has label");
25277             cfg.cn = [
25278                 {
25279                     tag: 'label',
25280                     'for' :  id,
25281                     cls : 'control-label',
25282                     html : this.fieldLabel
25283                 },
25284                 {
25285                     cls : "", 
25286                     cn: [
25287                         inputblock
25288                     ]
25289                 }
25290             ];
25291             
25292             if (boxLabelCfg) {
25293                 cfg.cn[1].cn.push(boxLabelCfg);
25294             }
25295             
25296             if(this.labelWidth > 12){
25297                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25298             }
25299             
25300             if(this.labelWidth < 13 && this.labelmd == 0){
25301                 this.labelmd = this.labelWidth;
25302             }
25303             
25304             if(this.labellg > 0){
25305                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25306                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25307             }
25308             
25309             if(this.labelmd > 0){
25310                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25311                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25312             }
25313             
25314             if(this.labelsm > 0){
25315                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25316                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25317             }
25318             
25319             if(this.labelxs > 0){
25320                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25321                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25322             }
25323             
25324         } else if ( this.fieldLabel.length) {
25325 //                Roo.log(" label");
25326                 cfg.cn = [
25327                    
25328                     {
25329                         tag: this.boxLabel ? 'span' : 'label',
25330                         'for': id,
25331                         cls: 'control-label box-input-label',
25332                         //cls : 'input-group-addon',
25333                         html : this.fieldLabel
25334                     },
25335                     
25336                     inputblock
25337                     
25338                 ];
25339                 if (boxLabelCfg) {
25340                     cfg.cn.push(boxLabelCfg);
25341                 }
25342
25343         } else {
25344             
25345 //                Roo.log(" no label && no align");
25346                 cfg.cn = [  inputblock ] ;
25347                 if (boxLabelCfg) {
25348                     cfg.cn.push(boxLabelCfg);
25349                 }
25350
25351                 
25352         }
25353         
25354        
25355         
25356         if(this.inputType != 'radio'){
25357             cfg.cn.push(hidden);
25358         }
25359         
25360         return cfg;
25361         
25362     },
25363     
25364     /**
25365      * return the real input element.
25366      */
25367     inputEl: function ()
25368     {
25369         return this.el.select('input.roo-' + this.inputType,true).first();
25370     },
25371     hiddenEl: function ()
25372     {
25373         return this.el.select('input.roo-hidden-value',true).first();
25374     },
25375     
25376     labelEl: function()
25377     {
25378         return this.el.select('label.control-label',true).first();
25379     },
25380     /* depricated... */
25381     
25382     label: function()
25383     {
25384         return this.labelEl();
25385     },
25386     
25387     boxLabelEl: function()
25388     {
25389         return this.el.select('label.box-label',true).first();
25390     },
25391     
25392     initEvents : function()
25393     {
25394 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25395         
25396         this.inputEl().on('click', this.onClick,  this);
25397         
25398         if (this.boxLabel) { 
25399             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25400         }
25401         
25402         this.startValue = this.getValue();
25403         
25404         if(this.groupId){
25405             Roo.bootstrap.form.CheckBox.register(this);
25406         }
25407     },
25408     
25409     onClick : function(e)
25410     {   
25411         if(this.fireEvent('click', this, e) !== false){
25412             this.setChecked(!this.checked);
25413         }
25414         
25415     },
25416     
25417     setChecked : function(state,suppressEvent)
25418     {
25419         this.startValue = this.getValue();
25420
25421         if(this.inputType == 'radio'){
25422             
25423             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25424                 e.dom.checked = false;
25425             });
25426             
25427             this.inputEl().dom.checked = true;
25428             
25429             this.inputEl().dom.value = this.inputValue;
25430             
25431             if(suppressEvent !== true){
25432                 this.fireEvent('check', this, true);
25433             }
25434             
25435             this.validate();
25436             
25437             return;
25438         }
25439         
25440         this.checked = state;
25441         
25442         this.inputEl().dom.checked = state;
25443         
25444         
25445         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25446         
25447         if(suppressEvent !== true){
25448             this.fireEvent('check', this, state);
25449         }
25450         
25451         this.validate();
25452     },
25453     
25454     getValue : function()
25455     {
25456         if(this.inputType == 'radio'){
25457             return this.getGroupValue();
25458         }
25459         
25460         return this.hiddenEl().dom.value;
25461         
25462     },
25463     
25464     getGroupValue : function()
25465     {
25466         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25467             return '';
25468         }
25469         
25470         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25471     },
25472     
25473     setValue : function(v,suppressEvent)
25474     {
25475         if(this.inputType == 'radio'){
25476             this.setGroupValue(v, suppressEvent);
25477             return;
25478         }
25479         
25480         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25481         
25482         this.validate();
25483     },
25484     
25485     setGroupValue : function(v, suppressEvent)
25486     {
25487         this.startValue = this.getValue();
25488         
25489         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25490             e.dom.checked = false;
25491             
25492             if(e.dom.value == v){
25493                 e.dom.checked = true;
25494             }
25495         });
25496         
25497         if(suppressEvent !== true){
25498             this.fireEvent('check', this, true);
25499         }
25500
25501         this.validate();
25502         
25503         return;
25504     },
25505     
25506     validate : function()
25507     {
25508         if(this.getVisibilityEl().hasClass('hidden')){
25509             return true;
25510         }
25511         
25512         if(
25513                 this.disabled || 
25514                 (this.inputType == 'radio' && this.validateRadio()) ||
25515                 (this.inputType == 'checkbox' && this.validateCheckbox())
25516         ){
25517             this.markValid();
25518             return true;
25519         }
25520         
25521         this.markInvalid();
25522         return false;
25523     },
25524     
25525     validateRadio : function()
25526     {
25527         if(this.getVisibilityEl().hasClass('hidden')){
25528             return true;
25529         }
25530         
25531         if(this.allowBlank){
25532             return true;
25533         }
25534         
25535         var valid = false;
25536         
25537         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25538             if(!e.dom.checked){
25539                 return;
25540             }
25541             
25542             valid = true;
25543             
25544             return false;
25545         });
25546         
25547         return valid;
25548     },
25549     
25550     validateCheckbox : function()
25551     {
25552         if(!this.groupId){
25553             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25554             //return (this.getValue() == this.inputValue) ? true : false;
25555         }
25556         
25557         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25558         
25559         if(!group){
25560             return false;
25561         }
25562         
25563         var r = false;
25564         
25565         for(var i in group){
25566             if(group[i].el.isVisible(true)){
25567                 r = false;
25568                 break;
25569             }
25570             
25571             r = true;
25572         }
25573         
25574         for(var i in group){
25575             if(r){
25576                 break;
25577             }
25578             
25579             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25580         }
25581         
25582         return r;
25583     },
25584     
25585     /**
25586      * Mark this field as valid
25587      */
25588     markValid : function()
25589     {
25590         var _this = this;
25591         
25592         this.fireEvent('valid', this);
25593         
25594         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25595         
25596         if(this.groupId){
25597             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25598         }
25599         
25600         if(label){
25601             label.markValid();
25602         }
25603
25604         if(this.inputType == 'radio'){
25605             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25606                 var fg = e.findParent('.form-group', false, true);
25607                 if (Roo.bootstrap.version == 3) {
25608                     fg.removeClass([_this.invalidClass, _this.validClass]);
25609                     fg.addClass(_this.validClass);
25610                 } else {
25611                     fg.removeClass(['is-valid', 'is-invalid']);
25612                     fg.addClass('is-valid');
25613                 }
25614             });
25615             
25616             return;
25617         }
25618
25619         if(!this.groupId){
25620             var fg = this.el.findParent('.form-group', false, true);
25621             if (Roo.bootstrap.version == 3) {
25622                 fg.removeClass([this.invalidClass, this.validClass]);
25623                 fg.addClass(this.validClass);
25624             } else {
25625                 fg.removeClass(['is-valid', 'is-invalid']);
25626                 fg.addClass('is-valid');
25627             }
25628             return;
25629         }
25630         
25631         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25632         
25633         if(!group){
25634             return;
25635         }
25636         
25637         for(var i in group){
25638             var fg = group[i].el.findParent('.form-group', false, true);
25639             if (Roo.bootstrap.version == 3) {
25640                 fg.removeClass([this.invalidClass, this.validClass]);
25641                 fg.addClass(this.validClass);
25642             } else {
25643                 fg.removeClass(['is-valid', 'is-invalid']);
25644                 fg.addClass('is-valid');
25645             }
25646         }
25647     },
25648     
25649      /**
25650      * Mark this field as invalid
25651      * @param {String} msg The validation message
25652      */
25653     markInvalid : function(msg)
25654     {
25655         if(this.allowBlank){
25656             return;
25657         }
25658         
25659         var _this = this;
25660         
25661         this.fireEvent('invalid', this, msg);
25662         
25663         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25664         
25665         if(this.groupId){
25666             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25667         }
25668         
25669         if(label){
25670             label.markInvalid();
25671         }
25672             
25673         if(this.inputType == 'radio'){
25674             
25675             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25676                 var fg = e.findParent('.form-group', false, true);
25677                 if (Roo.bootstrap.version == 3) {
25678                     fg.removeClass([_this.invalidClass, _this.validClass]);
25679                     fg.addClass(_this.invalidClass);
25680                 } else {
25681                     fg.removeClass(['is-invalid', 'is-valid']);
25682                     fg.addClass('is-invalid');
25683                 }
25684             });
25685             
25686             return;
25687         }
25688         
25689         if(!this.groupId){
25690             var fg = this.el.findParent('.form-group', false, true);
25691             if (Roo.bootstrap.version == 3) {
25692                 fg.removeClass([_this.invalidClass, _this.validClass]);
25693                 fg.addClass(_this.invalidClass);
25694             } else {
25695                 fg.removeClass(['is-invalid', 'is-valid']);
25696                 fg.addClass('is-invalid');
25697             }
25698             return;
25699         }
25700         
25701         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25702         
25703         if(!group){
25704             return;
25705         }
25706         
25707         for(var i in group){
25708             var fg = group[i].el.findParent('.form-group', false, true);
25709             if (Roo.bootstrap.version == 3) {
25710                 fg.removeClass([_this.invalidClass, _this.validClass]);
25711                 fg.addClass(_this.invalidClass);
25712             } else {
25713                 fg.removeClass(['is-invalid', 'is-valid']);
25714                 fg.addClass('is-invalid');
25715             }
25716         }
25717         
25718     },
25719     
25720     clearInvalid : function()
25721     {
25722         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25723         
25724         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25725         
25726         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25727         
25728         if (label && label.iconEl) {
25729             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25730             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25731         }
25732     },
25733     
25734     disable : function()
25735     {
25736         if(this.inputType != 'radio'){
25737             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25738             return;
25739         }
25740         
25741         var _this = this;
25742         
25743         if(this.rendered){
25744             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25745                 _this.getActionEl().addClass(this.disabledClass);
25746                 e.dom.disabled = true;
25747             });
25748         }
25749         
25750         this.disabled = true;
25751         this.fireEvent("disable", this);
25752         return this;
25753     },
25754
25755     enable : function()
25756     {
25757         if(this.inputType != 'radio'){
25758             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25759             return;
25760         }
25761         
25762         var _this = this;
25763         
25764         if(this.rendered){
25765             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25766                 _this.getActionEl().removeClass(this.disabledClass);
25767                 e.dom.disabled = false;
25768             });
25769         }
25770         
25771         this.disabled = false;
25772         this.fireEvent("enable", this);
25773         return this;
25774     },
25775     
25776     setBoxLabel : function(v)
25777     {
25778         this.boxLabel = v;
25779         
25780         if(this.rendered){
25781             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25782         }
25783     }
25784
25785 });
25786
25787 Roo.apply(Roo.bootstrap.form.CheckBox, {
25788     
25789     groups: {},
25790     
25791      /**
25792     * register a CheckBox Group
25793     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25794     */
25795     register : function(checkbox)
25796     {
25797         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25798             this.groups[checkbox.groupId] = {};
25799         }
25800         
25801         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25802             return;
25803         }
25804         
25805         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25806         
25807     },
25808     /**
25809     * fetch a CheckBox Group based on the group ID
25810     * @param {string} the group ID
25811     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25812     */
25813     get: function(groupId) {
25814         if (typeof(this.groups[groupId]) == 'undefined') {
25815             return false;
25816         }
25817         
25818         return this.groups[groupId] ;
25819     }
25820     
25821     
25822 });
25823 /*
25824  * - LGPL
25825  *
25826  * RadioItem
25827  * 
25828  */
25829
25830 /**
25831  * @class Roo.bootstrap.form.Radio
25832  * @extends Roo.bootstrap.Component
25833  * Bootstrap Radio class
25834  * @cfg {String} boxLabel - the label associated
25835  * @cfg {String} value - the value of radio
25836  * 
25837  * @constructor
25838  * Create a new Radio
25839  * @param {Object} config The config object
25840  */
25841 Roo.bootstrap.form.Radio = function(config){
25842     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25843     
25844 };
25845
25846 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25847     
25848     boxLabel : '',
25849     
25850     value : '',
25851     
25852     getAutoCreate : function()
25853     {
25854         var cfg = {
25855             tag : 'div',
25856             cls : 'form-group radio',
25857             cn : [
25858                 {
25859                     tag : 'label',
25860                     cls : 'box-label',
25861                     html : this.boxLabel
25862                 }
25863             ]
25864         };
25865         
25866         return cfg;
25867     },
25868     
25869     initEvents : function() 
25870     {
25871         this.parent().register(this);
25872         
25873         this.el.on('click', this.onClick, this);
25874         
25875     },
25876     
25877     onClick : function(e)
25878     {
25879         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25880             this.setChecked(true);
25881         }
25882     },
25883     
25884     setChecked : function(state, suppressEvent)
25885     {
25886         this.parent().setValue(this.value, suppressEvent);
25887         
25888     },
25889     
25890     setBoxLabel : function(v)
25891     {
25892         this.boxLabel = v;
25893         
25894         if(this.rendered){
25895             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25896         }
25897     }
25898     
25899 });
25900  
25901
25902  /*
25903  * - LGPL
25904  *
25905  * Input
25906  * 
25907  */
25908
25909 /**
25910  * @class Roo.bootstrap.form.SecurePass
25911  * @extends Roo.bootstrap.form.Input
25912  * Bootstrap SecurePass class
25913  * @cfg {Number} minimumStrength invalid if the strength of the password input is less than the minimum strength (from 0 to 3) (default 2)
25914  *
25915  * 
25916  * @constructor
25917  * Create a new SecurePass
25918  * @param {Object} config The config object
25919  */
25920  
25921 Roo.bootstrap.form.SecurePass = function (config) {
25922     
25923     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25924 }
25925
25926 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25927     minimumStrength : 2,
25928     // private
25929     meterWidth: 300, 
25930     imageRoot: '/',  
25931     // private
25932     strength: 0,
25933     // private
25934     _lastPwd: null,
25935     // private
25936     kCapitalLetter: 0,
25937     kSmallLetter: 1,
25938     kDigit: 2,
25939     kPunctuation: 3,
25940     
25941     insecure: false,
25942     // private
25943     initEvents: function ()
25944     {
25945         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25946
25947         if (this.el.is('input[type=password]') && Roo.isSafari) {
25948             this.el.on('keydown', this.SafariOnKeyDown, this);
25949         }
25950
25951         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25952     },
25953     // private
25954     onRender: function (ct, position)
25955     {
25956         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25957         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25958         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25959
25960         this.trigger.createChild({
25961                    cn: [
25962                     {
25963                     //id: 'PwdMeter',
25964                     tag: 'div',
25965                     cls: 'roo-password-meter-grey col-xs-12',
25966                     style: {
25967                         //width: 0,
25968                         //width: this.meterWidth + 'px'                                                
25969                         }
25970                     },
25971                     {                            
25972                          cls: 'roo-password-meter-text'                          
25973                     }
25974                 ]            
25975         });
25976
25977          
25978         if (this.hideTrigger) {
25979             this.trigger.setDisplayed(false);
25980         }
25981         this.setSize(this.width || '', this.height || '');
25982     },
25983     // private
25984     onDestroy: function ()
25985     {
25986         if (this.trigger) {
25987             this.trigger.removeAllListeners();
25988             this.trigger.remove();
25989         }
25990         if (this.wrap) {
25991             this.wrap.remove();
25992         }
25993         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25994     },
25995     // private
25996     checkStrength: function ()
25997     {
25998         var pwd = this.inputEl().getValue();
25999         if (pwd == this._lastPwd) {
26000             return;
26001         }
26002
26003         var strength;
26004         if (this.ClientSideStrongPassword(pwd)) {
26005             strength = 3;
26006         } else if (this.ClientSideMediumPassword(pwd)) {
26007             strength = 2;
26008         } else if (this.ClientSideWeakPassword(pwd)) {
26009             strength = 1;
26010         } else {
26011             strength = 0;
26012         }
26013         
26014         Roo.log('strength1: ' + strength);
26015         
26016         //var pm = this.trigger.child('div/div/div').dom;
26017         var pm = this.trigger.child('div/div');
26018         pm.removeClass(Roo.bootstrap.form.SecurePass.meterClass);
26019         pm.addClass(Roo.bootstrap.form.SecurePass.meterClass[strength]);
26020                 
26021         
26022         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
26023                 
26024         pt.innerHTML = Roo.bootstrap.form.SecurePass.meterLabel + '&nbsp;' + Roo.bootstrap.form.SecurePass.pwdStrengths[strength];
26025         
26026         this._lastPwd = pwd;
26027     },
26028     reset: function ()
26029     {
26030         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
26031         
26032         this._lastPwd = '';
26033         
26034         var pm = this.trigger.child('div/div');
26035         pm.removeClass(Roo.bootstrap.form.SecurePass.meterClass);
26036         pm.addClass('roo-password-meter-grey');        
26037         
26038         
26039         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
26040         
26041         pt.innerHTML = '';
26042         this.inputEl().dom.type='password';
26043     },
26044     // private
26045     validateValue: function (value)
26046     {
26047         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
26048             return false;
26049         }
26050         if (value.length == 0) {
26051             if (this.allowBlank) {
26052                 this.clearInvalid();
26053                 return true;
26054             }
26055
26056             this.invalidText = Roo.bootstrap.form.SecurePass.errors.PwdEmpty;
26057             return false;
26058         }
26059         
26060         if(this.insecure){
26061             return true;
26062         }
26063         
26064         if (!value.match(/[\x21-\x7e]+/)) {
26065             this.invalidText = Roo.bootstrap.form.SecurePass.errors.PwdBadChar;
26066             return false;
26067         }
26068         if (value.length < 6) {
26069             this.invalidText = Roo.bootstrap.form.SecurePass.errors.PwdShort;
26070             return false;
26071         }
26072         if (value.length > 16) {
26073             this.invalidText = Roo.bootstrap.form.SecurePass.errors.PwdLong;
26074             return false;
26075         }
26076         var strength;
26077         if (this.ClientSideStrongPassword(value)) {
26078             strength = 3;
26079         } else if (this.ClientSideMediumPassword(value)) {
26080             strength = 2;
26081         } else if (this.ClientSideWeakPassword(value)) {
26082             strength = 1;
26083         } else {
26084             strength = 0;
26085         }
26086
26087         
26088         if (strength < this.minimumStrength) {
26089             this.invalidText = Roo.bootstrap.form.SecurePass.errors.TooWeak;
26090             return false;
26091         }
26092         
26093         
26094         console.log('strength2: ' + strength);
26095         
26096         //var pm = this.trigger.child('div/div/div').dom;
26097         
26098         var pm = this.trigger.child('div/div');
26099         pm.removeClass(Roo.bootstrap.form.SecurePass.meterClass);
26100         pm.addClass(Roo.bootstrap.form.SecurePass.meterClass[strength]);
26101                 
26102         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
26103                 
26104         pt.innerHTML = Roo.bootstrap.form.SecurePass.meterLabel + '&nbsp;' + Roo.bootstrap.form.SecurePass.pwdStrengths[strength];
26105
26106         return true;
26107     },
26108     // private
26109     CharacterSetChecks: function (type)
26110     {
26111         this.type = type;
26112         this.fResult = false;
26113     },
26114     // private
26115     isctype: function (character, type)
26116     {
26117         switch (type) {  
26118             case this.kCapitalLetter:
26119                 if (character >= 'A' && character <= 'Z') {
26120                     return true;
26121                 }
26122                 break;
26123             
26124             case this.kSmallLetter:
26125                 if (character >= 'a' && character <= 'z') {
26126                     return true;
26127                 }
26128                 break;
26129             
26130             case this.kDigit:
26131                 if (character >= '0' && character <= '9') {
26132                     return true;
26133                 }
26134                 break;
26135             
26136             case this.kPunctuation:
26137                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
26138                     return true;
26139                 }
26140                 break;
26141             
26142             default:
26143                 return false;
26144         }
26145
26146     },
26147     // private
26148     IsLongEnough: function (pwd, size)
26149     {
26150         return !(pwd == null || isNaN(size) || pwd.length < size);
26151     },
26152     // private
26153     SpansEnoughCharacterSets: function (word, nb)
26154     {
26155         if (!this.IsLongEnough(word, nb))
26156         {
26157             return false;
26158         }
26159
26160         var characterSetChecks = new Array(
26161             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
26162             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
26163         );
26164         
26165         for (var index = 0; index < word.length; ++index) {
26166             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26167                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26168                     characterSetChecks[nCharSet].fResult = true;
26169                     break;
26170                 }
26171             }
26172         }
26173
26174         var nCharSets = 0;
26175         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26176             if (characterSetChecks[nCharSet].fResult) {
26177                 ++nCharSets;
26178             }
26179         }
26180
26181         if (nCharSets < nb) {
26182             return false;
26183         }
26184         return true;
26185     },
26186     // private
26187     ClientSideStrongPassword: function (pwd)
26188     {
26189         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26190     },
26191     // private
26192     ClientSideMediumPassword: function (pwd)
26193     {
26194         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26195     },
26196     // private
26197     ClientSideWeakPassword: function (pwd)
26198     {
26199         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26200     }
26201           
26202 });
26203
26204 Roo.bootstrap.form.SecurePass.errors = {
26205     PwdEmpty: "Please type a password, and then retype it to confirm.",
26206     PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
26207     PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
26208     PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
26209     IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
26210     FNInPwd: "Your password can't contain your first name. Please type a different password.",
26211     LNInPwd: "Your password can't contain your last name. Please type a different password.",
26212     TooWeak: "Your password is Too Weak."
26213 };
26214
26215 Roo.bootstrap.form.SecurePass.meterLabel = "Password strength:";
26216 Roo.bootstrap.form.SecurePass.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
26217 Roo.bootstrap.form.SecurePass.meterClass = [
26218     "roo-password-meter-tooweak", 
26219     "roo-password-meter-weak", 
26220     "roo-password-meter-medium", 
26221     "roo-password-meter-strong", 
26222     "roo-password-meter-grey"
26223 ];/**
26224  * @class Roo.bootstrap.form.Password
26225  * @extends Roo.bootstrap.form.Input
26226  * Bootstrap Password class
26227  * 
26228  * 
26229  * 
26230  * 
26231  * @constructor
26232  * Create a new Password
26233  * @param {Object} config The config object
26234  */
26235
26236 Roo.bootstrap.form.Password = function(config){
26237     Roo.bootstrap.form.Password.superclass.constructor.call(this, config);
26238
26239     this.inputType = 'password';
26240 };
26241
26242 Roo.extend(Roo.bootstrap.form.Password, Roo.bootstrap.form.Input, {
26243
26244     onRender : function(ct, position)
26245     {
26246         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
26247
26248         this.el.addClass('form-password');
26249
26250         this.wrap = this.inputEl().wrap({
26251             cls : 'password-wrap'
26252         });
26253
26254         this.toggle = this.wrap.createChild({
26255             tag : 'Button',
26256             cls : 'password-toggle'
26257         });
26258
26259
26260         this.toggleEl().addClass('password-hidden');
26261
26262         this.toggleEl().on('click', this.onToggleClick, this);;
26263     },
26264
26265     toggleEl: function()
26266     {
26267         return this.el.select('button.password-toggle',true).first();
26268     },
26269
26270     onToggleClick : function(e) 
26271     {
26272         var input = this.inputEl();
26273         var toggle = this.toggleEl();
26274
26275         toggle.removeClass(['password-visible', 'password-hidden']);
26276
26277         if(input.attr('type') == 'password') {
26278             input.attr('type', 'text');
26279             toggle.addClass('password-visible');
26280         }
26281         else {
26282             input.attr('type', 'password');
26283             toggle.addClass('password-hidden');
26284         }
26285     }
26286 });Roo.rtf = {}; // namespace
26287 Roo.rtf.Hex = function(hex)
26288 {
26289     this.hexstr = hex;
26290 };
26291 Roo.rtf.Paragraph = function(opts)
26292 {
26293     this.content = []; ///??? is that used?
26294 };Roo.rtf.Span = function(opts)
26295 {
26296     this.value = opts.value;
26297 };
26298
26299 Roo.rtf.Group = function(parent)
26300 {
26301     // we dont want to acutally store parent - it will make debug a nightmare..
26302     this.content = [];
26303     this.cn  = [];
26304      
26305        
26306     
26307 };
26308
26309 Roo.rtf.Group.prototype = {
26310     ignorable : false,
26311     content: false,
26312     cn: false,
26313     addContent : function(node) {
26314         // could set styles...
26315         this.content.push(node);
26316     },
26317     addChild : function(cn)
26318     {
26319         this.cn.push(cn);
26320     },
26321     // only for images really...
26322     toDataURL : function()
26323     {
26324         var mimetype = false;
26325         switch(true) {
26326             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26327                 mimetype = "image/png";
26328                 break;
26329              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26330                 mimetype = "image/jpeg";
26331                 break;
26332             default :
26333                 return 'about:blank'; // ?? error?
26334         }
26335         
26336         
26337         var hexstring = this.content[this.content.length-1].value;
26338         
26339         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26340             return String.fromCharCode(parseInt(a, 16));
26341         }).join(""));
26342     }
26343     
26344 };
26345 // this looks like it's normally the {rtf{ .... }}
26346 Roo.rtf.Document = function()
26347 {
26348     // we dont want to acutally store parent - it will make debug a nightmare..
26349     this.rtlch  = [];
26350     this.content = [];
26351     this.cn = [];
26352     
26353 };
26354 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26355     addChild : function(cn)
26356     {
26357         this.cn.push(cn);
26358         switch(cn.type) {
26359             case 'rtlch': // most content seems to be inside this??
26360             case 'listtext':
26361             case 'shpinst':
26362                 this.rtlch.push(cn);
26363                 return;
26364             default:
26365                 this[cn.type] = cn;
26366         }
26367         
26368     },
26369     
26370     getElementsByType : function(type)
26371     {
26372         var ret =  [];
26373         this._getElementsByType(type, ret, this.cn, 'rtf');
26374         return ret;
26375     },
26376     _getElementsByType : function (type, ret, search_array, path)
26377     {
26378         search_array.forEach(function(n,i) {
26379             if (n.type == type) {
26380                 n.path = path + '/' + n.type + ':' + i;
26381                 ret.push(n);
26382             }
26383             if (n.cn.length > 0) {
26384                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26385             }
26386         },this);
26387     }
26388     
26389 });
26390  
26391 Roo.rtf.Ctrl = function(opts)
26392 {
26393     this.value = opts.value;
26394     this.param = opts.param;
26395 };
26396 /**
26397  *
26398  *
26399  * based on this https://github.com/iarna/rtf-parser
26400  * it's really only designed to extract pict from pasted RTF 
26401  *
26402  * usage:
26403  *
26404  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26405  *  
26406  *
26407  */
26408
26409  
26410
26411
26412
26413 Roo.rtf.Parser = function(text) {
26414     //super({objectMode: true})
26415     this.text = '';
26416     this.parserState = this.parseText;
26417     
26418     // these are for interpeter...
26419     this.doc = {};
26420     ///this.parserState = this.parseTop
26421     this.groupStack = [];
26422     this.hexStore = [];
26423     this.doc = false;
26424     
26425     this.groups = []; // where we put the return.
26426     
26427     for (var ii = 0; ii < text.length; ++ii) {
26428         ++this.cpos;
26429         
26430         if (text[ii] === '\n') {
26431             ++this.row;
26432             this.col = 1;
26433         } else {
26434             ++this.col;
26435         }
26436         this.parserState(text[ii]);
26437     }
26438     
26439     
26440     
26441 };
26442 Roo.rtf.Parser.prototype = {
26443     text : '', // string being parsed..
26444     controlWord : '',
26445     controlWordParam :  '',
26446     hexChar : '',
26447     doc : false,
26448     group: false,
26449     groupStack : false,
26450     hexStore : false,
26451     
26452     
26453     cpos : 0, 
26454     row : 1, // reportin?
26455     col : 1, //
26456
26457      
26458     push : function (el)
26459     {
26460         var m = 'cmd'+ el.type;
26461         if (typeof(this[m]) == 'undefined') {
26462             Roo.log('invalid cmd:' + el.type);
26463             return;
26464         }
26465         this[m](el);
26466         //Roo.log(el);
26467     },
26468     flushHexStore : function()
26469     {
26470         if (this.hexStore.length < 1) {
26471             return;
26472         }
26473         var hexstr = this.hexStore.map(
26474             function(cmd) {
26475                 return cmd.value;
26476         }).join('');
26477         
26478         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26479               
26480             
26481         this.hexStore.splice(0)
26482         
26483     },
26484     
26485     cmdgroupstart : function()
26486     {
26487         this.flushHexStore();
26488         if (this.group) {
26489             this.groupStack.push(this.group);
26490         }
26491          // parent..
26492         if (this.doc === false) {
26493             this.group = this.doc = new Roo.rtf.Document();
26494             return;
26495             
26496         }
26497         this.group = new Roo.rtf.Group(this.group);
26498     },
26499     cmdignorable : function()
26500     {
26501         this.flushHexStore();
26502         this.group.ignorable = true;
26503     },
26504     cmdendparagraph : function()
26505     {
26506         this.flushHexStore();
26507         this.group.addContent(new Roo.rtf.Paragraph());
26508     },
26509     cmdgroupend : function ()
26510     {
26511         this.flushHexStore();
26512         var endingGroup = this.group;
26513         
26514         
26515         this.group = this.groupStack.pop();
26516         if (this.group) {
26517             this.group.addChild(endingGroup);
26518         }
26519         
26520         
26521         
26522         var doc = this.group || this.doc;
26523         //if (endingGroup instanceof FontTable) {
26524         //  doc.fonts = endingGroup.table
26525         //} else if (endingGroup instanceof ColorTable) {
26526         //  doc.colors = endingGroup.table
26527         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26528         if (endingGroup.ignorable === false) {
26529             //code
26530             this.groups.push(endingGroup);
26531            // Roo.log( endingGroup );
26532         }
26533             //Roo.each(endingGroup.content, function(item)) {
26534             //    doc.addContent(item);
26535             //}
26536             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26537         //}
26538     },
26539     cmdtext : function (cmd)
26540     {
26541         this.flushHexStore();
26542         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26543             //this.group = this.doc
26544             return;  // we really don't care about stray text...
26545         }
26546         this.group.addContent(new Roo.rtf.Span(cmd));
26547     },
26548     cmdcontrolword : function (cmd)
26549     {
26550         this.flushHexStore();
26551         if (!this.group.type) {
26552             this.group.type = cmd.value;
26553             return;
26554         }
26555         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26556         // we actually don't care about ctrl words...
26557         return ;
26558         /*
26559         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26560         if (this[method]) {
26561             this[method](cmd.param)
26562         } else {
26563             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26564         }
26565         */
26566     },
26567     cmdhexchar : function(cmd) {
26568         this.hexStore.push(cmd);
26569     },
26570     cmderror : function(cmd) {
26571         throw cmd.value;
26572     },
26573     
26574     /*
26575       _flush (done) {
26576         if (this.text !== '\u0000') this.emitText()
26577         done()
26578       }
26579       */
26580       
26581       
26582     parseText : function(c)
26583     {
26584         if (c === '\\') {
26585             this.parserState = this.parseEscapes;
26586         } else if (c === '{') {
26587             this.emitStartGroup();
26588         } else if (c === '}') {
26589             this.emitEndGroup();
26590         } else if (c === '\x0A' || c === '\x0D') {
26591             // cr/lf are noise chars
26592         } else {
26593             this.text += c;
26594         }
26595     },
26596     
26597     parseEscapes: function (c)
26598     {
26599         if (c === '\\' || c === '{' || c === '}') {
26600             this.text += c;
26601             this.parserState = this.parseText;
26602         } else {
26603             this.parserState = this.parseControlSymbol;
26604             this.parseControlSymbol(c);
26605         }
26606     },
26607     parseControlSymbol: function(c)
26608     {
26609         if (c === '~') {
26610             this.text += '\u00a0'; // nbsp
26611             this.parserState = this.parseText
26612         } else if (c === '-') {
26613              this.text += '\u00ad'; // soft hyphen
26614         } else if (c === '_') {
26615             this.text += '\u2011'; // non-breaking hyphen
26616         } else if (c === '*') {
26617             this.emitIgnorable();
26618             this.parserState = this.parseText;
26619         } else if (c === "'") {
26620             this.parserState = this.parseHexChar;
26621         } else if (c === '|') { // formula cacter
26622             this.emitFormula();
26623             this.parserState = this.parseText;
26624         } else if (c === ':') { // subentry in an index entry
26625             this.emitIndexSubEntry();
26626             this.parserState = this.parseText;
26627         } else if (c === '\x0a') {
26628             this.emitEndParagraph();
26629             this.parserState = this.parseText;
26630         } else if (c === '\x0d') {
26631             this.emitEndParagraph();
26632             this.parserState = this.parseText;
26633         } else {
26634             this.parserState = this.parseControlWord;
26635             this.parseControlWord(c);
26636         }
26637     },
26638     parseHexChar: function (c)
26639     {
26640         if (/^[A-Fa-f0-9]$/.test(c)) {
26641             this.hexChar += c;
26642             if (this.hexChar.length >= 2) {
26643               this.emitHexChar();
26644               this.parserState = this.parseText;
26645             }
26646             return;
26647         }
26648         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26649         this.parserState = this.parseText;
26650         
26651     },
26652     parseControlWord : function(c)
26653     {
26654         if (c === ' ') {
26655             this.emitControlWord();
26656             this.parserState = this.parseText;
26657         } else if (/^[-\d]$/.test(c)) {
26658             this.parserState = this.parseControlWordParam;
26659             this.controlWordParam += c;
26660         } else if (/^[A-Za-z]$/.test(c)) {
26661           this.controlWord += c;
26662         } else {
26663           this.emitControlWord();
26664           this.parserState = this.parseText;
26665           this.parseText(c);
26666         }
26667     },
26668     parseControlWordParam : function (c) {
26669         if (/^\d$/.test(c)) {
26670           this.controlWordParam += c;
26671         } else if (c === ' ') {
26672           this.emitControlWord();
26673           this.parserState = this.parseText;
26674         } else {
26675           this.emitControlWord();
26676           this.parserState = this.parseText;
26677           this.parseText(c);
26678         }
26679     },
26680     
26681     
26682     
26683     
26684     emitText : function () {
26685         if (this.text === '') {
26686             return;
26687         }
26688         this.push({
26689             type: 'text',
26690             value: this.text,
26691             pos: this.cpos,
26692             row: this.row,
26693             col: this.col
26694         });
26695         this.text = ''
26696     },
26697     emitControlWord : function ()
26698     {
26699         this.emitText();
26700         if (this.controlWord === '') {
26701             // do we want to track this - it seems just to cause problems.
26702             //this.emitError('empty control word');
26703         } else {
26704             this.push({
26705                   type: 'controlword',
26706                   value: this.controlWord,
26707                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26708                   pos: this.cpos,
26709                   row: this.row,
26710                   col: this.col
26711             });
26712         }
26713         this.controlWord = '';
26714         this.controlWordParam = '';
26715     },
26716     emitStartGroup : function ()
26717     {
26718         this.emitText();
26719         this.push({
26720             type: 'groupstart',
26721             pos: this.cpos,
26722             row: this.row,
26723             col: this.col
26724         });
26725     },
26726     emitEndGroup : function ()
26727     {
26728         this.emitText();
26729         this.push({
26730             type: 'groupend',
26731             pos: this.cpos,
26732             row: this.row,
26733             col: this.col
26734         });
26735     },
26736     emitIgnorable : function ()
26737     {
26738         this.emitText();
26739         this.push({
26740             type: 'ignorable',
26741             pos: this.cpos,
26742             row: this.row,
26743             col: this.col
26744         });
26745     },
26746     emitHexChar : function ()
26747     {
26748         this.emitText();
26749         this.push({
26750             type: 'hexchar',
26751             value: this.hexChar,
26752             pos: this.cpos,
26753             row: this.row,
26754             col: this.col
26755         });
26756         this.hexChar = ''
26757     },
26758     emitError : function (message)
26759     {
26760       this.emitText();
26761       this.push({
26762             type: 'error',
26763             value: message,
26764             row: this.row,
26765             col: this.col,
26766             char: this.cpos //,
26767             //stack: new Error().stack
26768         });
26769     },
26770     emitEndParagraph : function () {
26771         this.emitText();
26772         this.push({
26773             type: 'endparagraph',
26774             pos: this.cpos,
26775             row: this.row,
26776             col: this.col
26777         });
26778     }
26779      
26780 } ; 
26781 /**
26782  * @class Roo.htmleditor.Filter
26783  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26784  * @cfg {DomElement} node The node to iterate and filter
26785  * @cfg {boolean|String|Array} tag Tags to replace 
26786  * @constructor
26787  * Create a new Filter.
26788  * @param {Object} config Configuration options
26789  */
26790
26791
26792
26793 Roo.htmleditor.Filter = function(cfg) {
26794     Roo.apply(this.cfg);
26795     // this does not actually call walk as it's really just a abstract class
26796 }
26797
26798
26799 Roo.htmleditor.Filter.prototype = {
26800     
26801     node: false,
26802     
26803     tag: false,
26804
26805     // overrride to do replace comments.
26806     replaceComment : false,
26807     
26808     // overrride to do replace or do stuff with tags..
26809     replaceTag : false,
26810     
26811     walk : function(dom)
26812     {
26813         Roo.each( Array.from(dom.childNodes), function( e ) {
26814             switch(true) {
26815                 
26816                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26817                     this.replaceComment(e);
26818                     return;
26819                 
26820                 case e.nodeType != 1: //not a node.
26821                     return;
26822                 
26823                 case this.tag === true: // everything
26824                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26825                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26826                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26827                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26828                     if (this.replaceTag && false === this.replaceTag(e)) {
26829                         return;
26830                     }
26831                     if (e.hasChildNodes()) {
26832                         this.walk(e);
26833                     }
26834                     return;
26835                 
26836                 default:    // tags .. that do not match.
26837                     if (e.hasChildNodes()) {
26838                         this.walk(e);
26839                     }
26840             }
26841             
26842         }, this);
26843         
26844     },
26845     
26846     
26847     removeNodeKeepChildren : function( node)
26848     {
26849     
26850         ar = Array.from(node.childNodes);
26851         for (var i = 0; i < ar.length; i++) {
26852          
26853             node.removeChild(ar[i]);
26854             // what if we need to walk these???
26855             node.parentNode.insertBefore(ar[i], node);
26856            
26857         }
26858         node.parentNode.removeChild(node);
26859     }
26860 }; 
26861
26862 /**
26863  * @class Roo.htmleditor.FilterAttributes
26864  * clean attributes and  styles including http:// etc.. in attribute
26865  * @constructor
26866 * Run a new Attribute Filter
26867 * @param {Object} config Configuration options
26868  */
26869 Roo.htmleditor.FilterAttributes = function(cfg)
26870 {
26871     Roo.apply(this, cfg);
26872     this.attrib_black = this.attrib_black || [];
26873     this.attrib_white = this.attrib_white || [];
26874
26875     this.attrib_clean = this.attrib_clean || [];
26876     this.style_white = this.style_white || [];
26877     this.style_black = this.style_black || [];
26878     this.walk(cfg.node);
26879 }
26880
26881 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26882 {
26883     tag: true, // all tags
26884     
26885     attrib_black : false, // array
26886     attrib_clean : false,
26887     attrib_white : false,
26888
26889     style_white : false,
26890     style_black : false,
26891      
26892      
26893     replaceTag : function(node)
26894     {
26895         if (!node.attributes || !node.attributes.length) {
26896             return true;
26897         }
26898         
26899         for (var i = node.attributes.length-1; i > -1 ; i--) {
26900             var a = node.attributes[i];
26901             //console.log(a);
26902             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26903                 node.removeAttribute(a.name);
26904                 continue;
26905             }
26906             
26907             
26908             
26909             if (a.name.toLowerCase().substr(0,2)=='on')  {
26910                 node.removeAttribute(a.name);
26911                 continue;
26912             }
26913             
26914             
26915             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26916                 node.removeAttribute(a.name);
26917                 continue;
26918             }
26919             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26920                 this.cleanAttr(node,a.name,a.value); // fixme..
26921                 continue;
26922             }
26923             if (a.name == 'style') {
26924                 this.cleanStyle(node,a.name,a.value);
26925                 continue;
26926             }
26927             /// clean up MS crap..
26928             // tecnically this should be a list of valid class'es..
26929             
26930             
26931             if (a.name == 'class') {
26932                 if (a.value.match(/^Mso/)) {
26933                     node.removeAttribute('class');
26934                 }
26935                 
26936                 if (a.value.match(/^body$/)) {
26937                     node.removeAttribute('class');
26938                 }
26939                 continue;
26940             }
26941             
26942             
26943             // style cleanup!?
26944             // class cleanup?
26945             
26946         }
26947         return true; // clean children
26948     },
26949         
26950     cleanAttr: function(node, n,v)
26951     {
26952         
26953         if (v.match(/^\./) || v.match(/^\//)) {
26954             return;
26955         }
26956         if (v.match(/^(http|https):\/\//)
26957             || v.match(/^mailto:/) 
26958             || v.match(/^ftp:/)
26959             || v.match(/^data:/)
26960             ) {
26961             return;
26962         }
26963         if (v.match(/^#/)) {
26964             return;
26965         }
26966         if (v.match(/^\{/)) { // allow template editing.
26967             return;
26968         }
26969 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26970         node.removeAttribute(n);
26971         
26972     },
26973     cleanStyle : function(node,  n,v)
26974     {
26975         if (v.match(/expression/)) { //XSS?? should we even bother..
26976             node.removeAttribute(n);
26977             return;
26978         }
26979         
26980         var parts = v.split(/;/);
26981         var clean = [];
26982         
26983         Roo.each(parts, function(p) {
26984             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26985             if (!p.length) {
26986                 return true;
26987             }
26988             var l = p.split(':').shift().replace(/\s+/g,'');
26989             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26990             
26991             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26992                 return true;
26993             }
26994             //Roo.log()
26995             // only allow 'c whitelisted system attributes'
26996             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26997                 return true;
26998             }
26999             
27000             
27001             clean.push(p);
27002             return true;
27003         },this);
27004         if (clean.length) { 
27005             node.setAttribute(n, clean.join(';'));
27006         } else {
27007             node.removeAttribute(n);
27008         }
27009         
27010     }
27011         
27012         
27013         
27014     
27015 });/**
27016  * @class Roo.htmleditor.FilterBlack
27017  * remove blacklisted elements.
27018  * @constructor
27019  * Run a new Blacklisted Filter
27020  * @param {Object} config Configuration options
27021  */
27022
27023 Roo.htmleditor.FilterBlack = function(cfg)
27024 {
27025     Roo.apply(this, cfg);
27026     this.walk(cfg.node);
27027 }
27028
27029 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
27030 {
27031     tag : true, // all elements.
27032    
27033     replaceTag : function(n)
27034     {
27035         n.parentNode.removeChild(n);
27036     }
27037 });
27038 /**
27039  * @class Roo.htmleditor.FilterComment
27040  * remove comments.
27041  * @constructor
27042 * Run a new Comments Filter
27043 * @param {Object} config Configuration options
27044  */
27045 Roo.htmleditor.FilterComment = function(cfg)
27046 {
27047     this.walk(cfg.node);
27048 }
27049
27050 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
27051 {
27052   
27053     replaceComment : function(n)
27054     {
27055         n.parentNode.removeChild(n);
27056     }
27057 });/**
27058  * @class Roo.htmleditor.FilterKeepChildren
27059  * remove tags but keep children
27060  * @constructor
27061  * Run a new Keep Children Filter
27062  * @param {Object} config Configuration options
27063  */
27064
27065 Roo.htmleditor.FilterKeepChildren = function(cfg)
27066 {
27067     Roo.apply(this, cfg);
27068     if (this.tag === false) {
27069         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
27070     }
27071     // hacky?
27072     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
27073         this.cleanNamespace = true;
27074     }
27075         
27076     this.walk(cfg.node);
27077 }
27078
27079 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
27080 {
27081     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
27082   
27083     replaceTag : function(node)
27084     {
27085         // walk children...
27086         //Roo.log(node.tagName);
27087         var ar = Array.from(node.childNodes);
27088         //remove first..
27089         
27090         for (var i = 0; i < ar.length; i++) {
27091             var e = ar[i];
27092             if (e.nodeType == 1) {
27093                 if (
27094                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
27095                     || // array and it matches
27096                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
27097                     ||
27098                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
27099                     ||
27100                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
27101                 ) {
27102                     this.replaceTag(ar[i]); // child is blacklisted as well...
27103                     continue;
27104                 }
27105             }
27106         }  
27107         ar = Array.from(node.childNodes);
27108         for (var i = 0; i < ar.length; i++) {
27109          
27110             node.removeChild(ar[i]);
27111             // what if we need to walk these???
27112             node.parentNode.insertBefore(ar[i], node);
27113             if (this.tag !== false) {
27114                 this.walk(ar[i]);
27115                 
27116             }
27117         }
27118         //Roo.log("REMOVE:" + node.tagName);
27119         node.parentNode.removeChild(node);
27120         return false; // don't walk children
27121         
27122         
27123     }
27124 });/**
27125  * @class Roo.htmleditor.FilterParagraph
27126  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
27127  * like on 'push' to remove the <p> tags and replace them with line breaks.
27128  * @constructor
27129  * Run a new Paragraph Filter
27130  * @param {Object} config Configuration options
27131  */
27132
27133 Roo.htmleditor.FilterParagraph = function(cfg)
27134 {
27135     // no need to apply config.
27136     this.walk(cfg.node);
27137 }
27138
27139 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
27140 {
27141     
27142      
27143     tag : 'P',
27144     
27145      
27146     replaceTag : function(node)
27147     {
27148         
27149         if (node.childNodes.length == 1 &&
27150             node.childNodes[0].nodeType == 3 &&
27151             node.childNodes[0].textContent.trim().length < 1
27152             ) {
27153             // remove and replace with '<BR>';
27154             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
27155             return false; // no need to walk..
27156         }
27157         var ar = Array.from(node.childNodes);
27158         for (var i = 0; i < ar.length; i++) {
27159             node.removeChild(ar[i]);
27160             // what if we need to walk these???
27161             node.parentNode.insertBefore(ar[i], node);
27162         }
27163         // now what about this?
27164         // <p> &nbsp; </p>
27165         
27166         // double BR.
27167         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
27168         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
27169         node.parentNode.removeChild(node);
27170         
27171         return false;
27172
27173     }
27174     
27175 });/**
27176  * @class Roo.htmleditor.FilterSpan
27177  * filter span's with no attributes out..
27178  * @constructor
27179  * Run a new Span Filter
27180  * @param {Object} config Configuration options
27181  */
27182
27183 Roo.htmleditor.FilterSpan = function(cfg)
27184 {
27185     // no need to apply config.
27186     this.walk(cfg.node);
27187 }
27188
27189 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
27190 {
27191      
27192     tag : 'SPAN',
27193      
27194  
27195     replaceTag : function(node)
27196     {
27197         if (node.attributes && node.attributes.length > 0) {
27198             return true; // walk if there are any.
27199         }
27200         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
27201         return false;
27202      
27203     }
27204     
27205 });/**
27206  * @class Roo.htmleditor.FilterTableWidth
27207   try and remove table width data - as that frequently messes up other stuff.
27208  * 
27209  *      was cleanTableWidths.
27210  *
27211  * Quite often pasting from word etc.. results in tables with column and widths.
27212  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27213  *
27214  * @constructor
27215  * Run a new Table Filter
27216  * @param {Object} config Configuration options
27217  */
27218
27219 Roo.htmleditor.FilterTableWidth = function(cfg)
27220 {
27221     // no need to apply config.
27222     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
27223     this.walk(cfg.node);
27224 }
27225
27226 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
27227 {
27228      
27229      
27230     
27231     replaceTag: function(node) {
27232         
27233         
27234       
27235         if (node.hasAttribute('width')) {
27236             node.removeAttribute('width');
27237         }
27238         
27239          
27240         if (node.hasAttribute("style")) {
27241             // pretty basic...
27242             
27243             var styles = node.getAttribute("style").split(";");
27244             var nstyle = [];
27245             Roo.each(styles, function(s) {
27246                 if (!s.match(/:/)) {
27247                     return;
27248                 }
27249                 var kv = s.split(":");
27250                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27251                     return;
27252                 }
27253                 // what ever is left... we allow.
27254                 nstyle.push(s);
27255             });
27256             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27257             if (!nstyle.length) {
27258                 node.removeAttribute('style');
27259             }
27260         }
27261         
27262         return true; // continue doing children..
27263     }
27264 });/**
27265  * @class Roo.htmleditor.FilterWord
27266  * try and clean up all the mess that Word generates.
27267  * 
27268  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
27269  
27270  * @constructor
27271  * Run a new Span Filter
27272  * @param {Object} config Configuration options
27273  */
27274
27275 Roo.htmleditor.FilterWord = function(cfg)
27276 {
27277     // no need to apply config.
27278     this.replaceDocBullets(cfg.node);
27279     
27280     this.replaceAname(cfg.node);
27281     // this is disabled as the removal is done by other filters;
27282    // this.walk(cfg.node);
27283     this.replaceImageTable(cfg.node);
27284     
27285 }
27286
27287 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27288 {
27289     tag: true,
27290      
27291     
27292     /**
27293      * Clean up MS wordisms...
27294      */
27295     replaceTag : function(node)
27296     {
27297          
27298         // no idea what this does - span with text, replaceds with just text.
27299         if(
27300                 node.nodeName == 'SPAN' &&
27301                 !node.hasAttributes() &&
27302                 node.childNodes.length == 1 &&
27303                 node.firstChild.nodeName == "#text"  
27304         ) {
27305             var textNode = node.firstChild;
27306             node.removeChild(textNode);
27307             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27308                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27309             }
27310             node.parentNode.insertBefore(textNode, node);
27311             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27312                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27313             }
27314             
27315             node.parentNode.removeChild(node);
27316             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27317         }
27318         
27319    
27320         
27321         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27322             node.parentNode.removeChild(node);
27323             return false; // dont do chidlren
27324         }
27325         //Roo.log(node.tagName);
27326         // remove - but keep children..
27327         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27328             //Roo.log('-- removed');
27329             while (node.childNodes.length) {
27330                 var cn = node.childNodes[0];
27331                 node.removeChild(cn);
27332                 node.parentNode.insertBefore(cn, node);
27333                 // move node to parent - and clean it..
27334                 if (cn.nodeType == 1) {
27335                     this.replaceTag(cn);
27336                 }
27337                 
27338             }
27339             node.parentNode.removeChild(node);
27340             /// no need to iterate chidlren = it's got none..
27341             //this.iterateChildren(node, this.cleanWord);
27342             return false; // no need to iterate children.
27343         }
27344         // clean styles
27345         if (node.className.length) {
27346             
27347             var cn = node.className.split(/\W+/);
27348             var cna = [];
27349             Roo.each(cn, function(cls) {
27350                 if (cls.match(/Mso[a-zA-Z]+/)) {
27351                     return;
27352                 }
27353                 cna.push(cls);
27354             });
27355             node.className = cna.length ? cna.join(' ') : '';
27356             if (!cna.length) {
27357                 node.removeAttribute("class");
27358             }
27359         }
27360         
27361         if (node.hasAttribute("lang")) {
27362             node.removeAttribute("lang");
27363         }
27364         
27365         if (node.hasAttribute("style")) {
27366             
27367             var styles = node.getAttribute("style").split(";");
27368             var nstyle = [];
27369             Roo.each(styles, function(s) {
27370                 if (!s.match(/:/)) {
27371                     return;
27372                 }
27373                 var kv = s.split(":");
27374                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27375                     return;
27376                 }
27377                 // what ever is left... we allow.
27378                 nstyle.push(s);
27379             });
27380             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27381             if (!nstyle.length) {
27382                 node.removeAttribute('style');
27383             }
27384         }
27385         return true; // do children
27386         
27387         
27388         
27389     },
27390     
27391     styleToObject: function(node)
27392     {
27393         var styles = (node.getAttribute("style") || '').split(";");
27394         var ret = {};
27395         Roo.each(styles, function(s) {
27396             if (!s.match(/:/)) {
27397                 return;
27398             }
27399             var kv = s.split(":");
27400              
27401             // what ever is left... we allow.
27402             ret[kv[0].trim()] = kv[1];
27403         });
27404         return ret;
27405     },
27406     
27407     
27408     replaceAname : function (doc)
27409     {
27410         // replace all the a/name without..
27411         var aa = Array.from(doc.getElementsByTagName('a'));
27412         for (var i = 0; i  < aa.length; i++) {
27413             var a = aa[i];
27414             if (a.hasAttribute("name")) {
27415                 a.removeAttribute("name");
27416             }
27417             if (a.hasAttribute("href")) {
27418                 continue;
27419             }
27420             // reparent children.
27421             this.removeNodeKeepChildren(a);
27422             
27423         }
27424         
27425         
27426         
27427     },
27428
27429     
27430     
27431     replaceDocBullets : function(doc)
27432     {
27433         // this is a bit odd - but it appears some indents use ql-indent-1
27434          //Roo.log(doc.innerHTML);
27435         
27436         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27437         for( var i = 0; i < listpara.length; i ++) {
27438             listpara[i].className = "MsoListParagraph";
27439         }
27440         
27441         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27442         for( var i = 0; i < listpara.length; i ++) {
27443             listpara[i].className = "MsoListParagraph";
27444         }
27445         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27446         for( var i = 0; i < listpara.length; i ++) {
27447             listpara[i].className = "MsoListParagraph";
27448         }
27449         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27450         for( var i = 0; i < listpara.length; i ++) {
27451             listpara[i].className = "MsoListParagraph";
27452         }
27453         
27454         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27455         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27456         for( var i = 0; i < htwo.length; i ++) {
27457             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27458                 htwo[i].className = "MsoListParagraph";
27459             }
27460         }
27461         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27462         for( var i = 0; i < listpara.length; i ++) {
27463             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27464                 listpara[i].className = "MsoListParagraph";
27465             } else {
27466                 listpara[i].className = "MsoNormalx";
27467             }
27468         }
27469        
27470         listpara = doc.getElementsByClassName('MsoListParagraph');
27471         // Roo.log(doc.innerHTML);
27472         
27473         
27474         
27475         while(listpara.length) {
27476             
27477             this.replaceDocBullet(listpara.item(0));
27478         }
27479       
27480     },
27481     
27482      
27483     
27484     replaceDocBullet : function(p)
27485     {
27486         // gather all the siblings.
27487         var ns = p,
27488             parent = p.parentNode,
27489             doc = parent.ownerDocument,
27490             items = [];
27491          
27492         //Roo.log("Parsing: " + p.innerText)    ;
27493         var listtype = 'ul';   
27494         while (ns) {
27495             if (ns.nodeType != 1) {
27496                 ns = ns.nextSibling;
27497                 continue;
27498             }
27499             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27500                 //Roo.log("Missing para r q1indent - got:" + ns.className);
27501                 break;
27502             }
27503             var spans = ns.getElementsByTagName('span');
27504             
27505             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27506                 items.push(ns);
27507                 ns = ns.nextSibling;
27508                 has_list = true;
27509                 if (!spans.length) {
27510                     continue;
27511                 }
27512                 var ff = '';
27513                 var se = spans[0];
27514                 for (var i = 0; i < spans.length;i++) {
27515                     se = spans[i];
27516                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
27517                         ff = se.style.fontFamily;
27518                         break;
27519                     }
27520                 }
27521                  
27522                     
27523                 //Roo.log("got font family: " + ff);
27524                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27525                     listtype = 'ol';
27526                 }
27527                 
27528                 continue;
27529             }
27530             //Roo.log("no mso-list?");
27531             
27532             var spans = ns.getElementsByTagName('span');
27533             if (!spans.length) {
27534                 break;
27535             }
27536             var has_list  = false;
27537             for(var i = 0; i < spans.length; i++) {
27538                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27539                     has_list = true;
27540                     break;
27541                 }
27542             }
27543             if (!has_list) {
27544                 break;
27545             }
27546             items.push(ns);
27547             ns = ns.nextSibling;
27548             
27549             
27550         }
27551         if (!items.length) {
27552             ns.className = "";
27553             return;
27554         }
27555         
27556         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27557         parent.insertBefore(ul, p);
27558         var lvl = 0;
27559         var stack = [ ul ];
27560         var last_li = false;
27561         
27562         var margin_to_depth = {};
27563         max_margins = -1;
27564         
27565         items.forEach(function(n, ipos) {
27566             //Roo.log("got innertHMLT=" + n.innerHTML);
27567             
27568             var spans = n.getElementsByTagName('span');
27569             if (!spans.length) {
27570                 //Roo.log("No spans found");
27571                  
27572                 parent.removeChild(n);
27573                 
27574                 
27575                 return; // skip it...
27576             }
27577            
27578                 
27579             var num = 1;
27580             var style = {};
27581             for(var i = 0; i < spans.length; i++) {
27582             
27583                 style = this.styleToObject(spans[i]);
27584                 if (typeof(style['mso-list']) == 'undefined') {
27585                     continue;
27586                 }
27587                 if (listtype == 'ol') {
27588                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27589                 }
27590                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27591                 break;
27592             }
27593             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27594             style = this.styleToObject(n); // mo-list is from the parent node.
27595             if (typeof(style['mso-list']) == 'undefined') {
27596                 //Roo.log("parent is missing level");
27597                   
27598                 parent.removeChild(n);
27599                  
27600                 return;
27601             }
27602             
27603             var margin = style['margin-left'];
27604             if (typeof(margin_to_depth[margin]) == 'undefined') {
27605                 max_margins++;
27606                 margin_to_depth[margin] = max_margins;
27607             }
27608             nlvl = margin_to_depth[margin] ;
27609              
27610             if (nlvl > lvl) {
27611                 //new indent
27612                 var nul = doc.createElement(listtype); // what about number lists...
27613                 if (!last_li) {
27614                     last_li = doc.createElement('li');
27615                     stack[lvl].appendChild(last_li);
27616                 }
27617                 last_li.appendChild(nul);
27618                 stack[nlvl] = nul;
27619                 
27620             }
27621             lvl = nlvl;
27622             
27623             // not starting at 1..
27624             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27625                 stack[nlvl].setAttribute("start", num);
27626             }
27627             
27628             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27629             last_li = nli;
27630             nli.innerHTML = n.innerHTML;
27631             //Roo.log("innerHTML = " + n.innerHTML);
27632             parent.removeChild(n);
27633             
27634              
27635              
27636             
27637         },this);
27638         
27639         
27640         
27641         
27642     },
27643     
27644     replaceImageTable : function(doc)
27645     {
27646          /*
27647           <table cellpadding=0 cellspacing=0 align=left>
27648   <tr>
27649    <td width=423 height=0></td>
27650   </tr>
27651   <tr>
27652    <td></td>
27653    <td><img width=601 height=401
27654    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27655    v:shapes="Picture_x0020_2"></td>
27656   </tr>
27657  </table>
27658  */
27659         var imgs = Array.from(doc.getElementsByTagName('img'));
27660         Roo.each(imgs, function(img) {
27661             var td = img.parentNode;
27662             if (td.nodeName !=  'TD') {
27663                 return;
27664             }
27665             var tr = td.parentNode;
27666             if (tr.nodeName !=  'TR') {
27667                 return;
27668             }
27669             var tbody = tr.parentNode;
27670             if (tbody.nodeName !=  'TBODY') {
27671                 return;
27672             }
27673             var table = tbody.parentNode;
27674             if (table.nodeName !=  'TABLE') {
27675                 return;
27676             }
27677             // first row..
27678             
27679             if (table.getElementsByTagName('tr').length != 2) {
27680                 return;
27681             }
27682             if (table.getElementsByTagName('td').length != 3) {
27683                 return;
27684             }
27685             if (table.innerText.trim() != '') {
27686                 return;
27687             }
27688             var p = table.parentNode;
27689             img.parentNode.removeChild(img);
27690             p.insertBefore(img, table);
27691             p.removeChild(table);
27692             
27693             
27694             
27695         });
27696         
27697       
27698     }
27699     
27700 });
27701 /**
27702  * @class Roo.htmleditor.FilterStyleToTag
27703  * part of the word stuff... - certain 'styles' should be converted to tags.
27704  * eg.
27705  *   font-weight: bold -> bold
27706  *   ?? super / subscrit etc..
27707  * 
27708  * @constructor
27709 * Run a new style to tag filter.
27710 * @param {Object} config Configuration options
27711  */
27712 Roo.htmleditor.FilterStyleToTag = function(cfg)
27713 {
27714     
27715     this.tags = {
27716         B  : [ 'fontWeight' , 'bold'],
27717         I :  [ 'fontStyle' , 'italic'],
27718         //pre :  [ 'font-style' , 'italic'],
27719         // h1.. h6 ?? font-size?
27720         SUP : [ 'verticalAlign' , 'super' ],
27721         SUB : [ 'verticalAlign' , 'sub' ]
27722         
27723         
27724     };
27725     
27726     Roo.apply(this, cfg);
27727      
27728     
27729     this.walk(cfg.node);
27730     
27731     
27732     
27733 }
27734
27735
27736 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27737 {
27738     tag: true, // all tags
27739     
27740     tags : false,
27741     
27742     
27743     replaceTag : function(node)
27744     {
27745         
27746         
27747         if (node.getAttribute("style") === null) {
27748             return true;
27749         }
27750         var inject = [];
27751         for (var k in this.tags) {
27752             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27753                 inject.push(k);
27754                 node.style.removeProperty(this.tags[k][0]);
27755             }
27756         }
27757         if (!inject.length) {
27758             return true; 
27759         }
27760         var cn = Array.from(node.childNodes);
27761         var nn = node;
27762         Roo.each(inject, function(t) {
27763             var nc = node.ownerDocument.createElement(t);
27764             nn.appendChild(nc);
27765             nn = nc;
27766         });
27767         for(var i = 0;i < cn.length;cn++) {
27768             node.removeChild(cn[i]);
27769             nn.appendChild(cn[i]);
27770         }
27771         return true /// iterate thru
27772     }
27773     
27774 })/**
27775  * @class Roo.htmleditor.FilterLongBr
27776  * BR/BR/BR - keep a maximum of 2...
27777  * @constructor
27778  * Run a new Long BR Filter
27779  * @param {Object} config Configuration options
27780  */
27781
27782 Roo.htmleditor.FilterLongBr = function(cfg)
27783 {
27784     // no need to apply config.
27785     this.walk(cfg.node);
27786 }
27787
27788 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27789 {
27790     
27791      
27792     tag : 'BR',
27793     
27794      
27795     replaceTag : function(node)
27796     {
27797         
27798         var ps = node.nextSibling;
27799         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27800             ps = ps.nextSibling;
27801         }
27802         
27803         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27804             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27805             return false;
27806         }
27807         
27808         if (!ps || ps.nodeType != 1) {
27809             return false;
27810         }
27811         
27812         if (!ps || ps.tagName != 'BR') {
27813            
27814             return false;
27815         }
27816         
27817         
27818         
27819         
27820         
27821         if (!node.previousSibling) {
27822             return false;
27823         }
27824         var ps = node.previousSibling;
27825         
27826         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27827             ps = ps.previousSibling;
27828         }
27829         if (!ps || ps.nodeType != 1) {
27830             return false;
27831         }
27832         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27833         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27834             return false;
27835         }
27836         
27837         node.parentNode.removeChild(node); // remove me...
27838         
27839         return false; // no need to do children
27840
27841     }
27842     
27843 }); 
27844
27845 /**
27846  * @class Roo.htmleditor.FilterBlock
27847  * removes id / data-block and contenteditable that are associated with blocks
27848  * usage should be done on a cloned copy of the dom
27849  * @constructor
27850 * Run a new Attribute Filter { node : xxxx }}
27851 * @param {Object} config Configuration options
27852  */
27853 Roo.htmleditor.FilterBlock = function(cfg)
27854 {
27855     Roo.apply(this, cfg);
27856     var qa = cfg.node.querySelectorAll;
27857     this.removeAttributes('data-block');
27858     this.removeAttributes('contenteditable');
27859     this.removeAttributes('id');
27860     
27861 }
27862
27863 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27864 {
27865     node: true, // all tags
27866      
27867      
27868     removeAttributes : function(attr)
27869     {
27870         var ar = this.node.querySelectorAll('*[' + attr + ']');
27871         for (var i =0;i<ar.length;i++) {
27872             ar[i].removeAttribute(attr);
27873         }
27874     }
27875         
27876         
27877         
27878     
27879 });
27880 /***
27881  * This is based loosely on tinymce 
27882  * @class Roo.htmleditor.TidySerializer
27883  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27884  * @constructor
27885  * @method Serializer
27886  * @param {Object} settings Name/value settings object.
27887  */
27888
27889
27890 Roo.htmleditor.TidySerializer = function(settings)
27891 {
27892     Roo.apply(this, settings);
27893     
27894     this.writer = new Roo.htmleditor.TidyWriter(settings);
27895     
27896     
27897
27898 };
27899 Roo.htmleditor.TidySerializer.prototype = {
27900     
27901     /**
27902      * @param {boolean} inner do the inner of the node.
27903      */
27904     inner : false,
27905     
27906     writer : false,
27907     
27908     /**
27909     * Serializes the specified node into a string.
27910     *
27911     * @example
27912     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27913     * @method serialize
27914     * @param {DomElement} node Node instance to serialize.
27915     * @return {String} String with HTML based on DOM tree.
27916     */
27917     serialize : function(node) {
27918         
27919         // = settings.validate;
27920         var writer = this.writer;
27921         var self  = this;
27922         this.handlers = {
27923             // #text
27924             3: function(node) {
27925                 
27926                 writer.text(node.nodeValue, node);
27927             },
27928             // #comment
27929             8: function(node) {
27930                 writer.comment(node.nodeValue);
27931             },
27932             // Processing instruction
27933             7: function(node) {
27934                 writer.pi(node.name, node.nodeValue);
27935             },
27936             // Doctype
27937             10: function(node) {
27938                 writer.doctype(node.nodeValue);
27939             },
27940             // CDATA
27941             4: function(node) {
27942                 writer.cdata(node.nodeValue);
27943             },
27944             // Document fragment
27945             11: function(node) {
27946                 node = node.firstChild;
27947                 if (!node) {
27948                     return;
27949                 }
27950                 while(node) {
27951                     self.walk(node);
27952                     node = node.nextSibling
27953                 }
27954             }
27955         };
27956         writer.reset();
27957         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27958         return writer.getContent();
27959     },
27960
27961     walk: function(node)
27962     {
27963         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27964             handler = this.handlers[node.nodeType];
27965             
27966         if (handler) {
27967             handler(node);
27968             return;
27969         }
27970     
27971         var name = node.nodeName;
27972         var isEmpty = node.childNodes.length < 1;
27973       
27974         var writer = this.writer;
27975         var attrs = node.attributes;
27976         // Sort attributes
27977         
27978         writer.start(node.nodeName, attrs, isEmpty, node);
27979         if (isEmpty) {
27980             return;
27981         }
27982         node = node.firstChild;
27983         if (!node) {
27984             writer.end(name);
27985             return;
27986         }
27987         while (node) {
27988             this.walk(node);
27989             node = node.nextSibling;
27990         }
27991         writer.end(name);
27992         
27993     
27994     }
27995     // Serialize element and treat all non elements as fragments
27996    
27997 }; 
27998
27999 /***
28000  * This is based loosely on tinymce 
28001  * @class Roo.htmleditor.TidyWriter
28002  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28003  *
28004  * Known issues?
28005  * - not tested much with 'PRE' formated elements.
28006  * 
28007  *
28008  *
28009  */
28010
28011 Roo.htmleditor.TidyWriter = function(settings)
28012 {
28013     
28014     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
28015     Roo.apply(this, settings);
28016     this.html = [];
28017     this.state = [];
28018      
28019     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
28020   
28021 }
28022 Roo.htmleditor.TidyWriter.prototype = {
28023
28024  
28025     state : false,
28026     
28027     indent :  '  ',
28028     
28029     // part of state...
28030     indentstr : '',
28031     in_pre: false,
28032     in_inline : false,
28033     last_inline : false,
28034     encode : false,
28035      
28036     
28037             /**
28038     * Writes the a start element such as <p id="a">.
28039     *
28040     * @method start
28041     * @param {String} name Name of the element.
28042     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
28043     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
28044     */
28045     start: function(name, attrs, empty, node)
28046     {
28047         var i, l, attr, value;
28048         
28049         // there are some situations where adding line break && indentation will not work. will not work.
28050         // <span / b / i ... formating?
28051         
28052         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
28053         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
28054         
28055         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
28056         
28057         var add_lb = name == 'BR' ? false : in_inline;
28058         
28059         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
28060             i_inline = false;
28061         }
28062
28063         var indentstr =  this.indentstr;
28064         
28065         // e_inline = elements that can be inline, but still allow \n before and after?
28066         // only 'BR' ??? any others?
28067         
28068         // ADD LINE BEFORE tage
28069         if (!this.in_pre) {
28070             if (in_inline) {
28071                 //code
28072                 if (name == 'BR') {
28073                     this.addLine();
28074                 } else if (this.lastElementEndsWS()) {
28075                     this.addLine();
28076                 } else{
28077                     // otherwise - no new line. (and dont indent.)
28078                     indentstr = '';
28079                 }
28080                 
28081             } else {
28082                 this.addLine();
28083             }
28084         } else {
28085             indentstr = '';
28086         }
28087         
28088         this.html.push(indentstr + '<', name.toLowerCase());
28089         
28090         if (attrs) {
28091             for (i = 0, l = attrs.length; i < l; i++) {
28092                 attr = attrs[i];
28093                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
28094             }
28095         }
28096      
28097         if (empty) {
28098             if (is_short) {
28099                 this.html[this.html.length] = '/>';
28100             } else {
28101                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
28102             }
28103             var e_inline = name == 'BR' ? false : this.in_inline;
28104             
28105             if (!e_inline && !this.in_pre) {
28106                 this.addLine();
28107             }
28108             return;
28109         
28110         }
28111         // not empty..
28112         this.html[this.html.length] = '>';
28113         
28114         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
28115         /*
28116         if (!in_inline && !in_pre) {
28117             var cn = node.firstChild;
28118             while(cn) {
28119                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
28120                     in_inline = true
28121                     break;
28122                 }
28123                 cn = cn.nextSibling;
28124             }
28125              
28126         }
28127         */
28128         
28129         
28130         this.pushState({
28131             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
28132             in_pre : in_pre,
28133             in_inline :  in_inline
28134         });
28135         // add a line after if we are not in a
28136         
28137         if (!in_inline && !in_pre) {
28138             this.addLine();
28139         }
28140         
28141             
28142          
28143         
28144     },
28145     
28146     lastElementEndsWS : function()
28147     {
28148         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
28149         if (value === false) {
28150             return true;
28151         }
28152         return value.match(/\s+$/);
28153         
28154     },
28155     
28156     /**
28157      * Writes the a end element such as </p>.
28158      *
28159      * @method end
28160      * @param {String} name Name of the element.
28161      */
28162     end: function(name) {
28163         var value;
28164         this.popState();
28165         var indentstr = '';
28166         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
28167         
28168         if (!this.in_pre && !in_inline) {
28169             this.addLine();
28170             indentstr  = this.indentstr;
28171         }
28172         this.html.push(indentstr + '</', name.toLowerCase(), '>');
28173         this.last_inline = in_inline;
28174         
28175         // pop the indent state..
28176     },
28177     /**
28178      * Writes a text node.
28179      *
28180      * In pre - we should not mess with the contents.
28181      * 
28182      *
28183      * @method text
28184      * @param {String} text String to write out.
28185      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
28186      */
28187     text: function(in_text, node)
28188     {
28189         // if not in whitespace critical
28190         if (in_text.length < 1) {
28191             return;
28192         }
28193         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
28194         
28195         if (this.in_pre) {
28196             this.html[this.html.length] =  text;
28197             return;   
28198         }
28199         
28200         if (this.in_inline) {
28201             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
28202             if (text != ' ') {
28203                 text = text.replace(/\s+/,' ');  // all white space to single white space
28204                 
28205                     
28206                 // if next tag is '<BR>', then we can trim right..
28207                 if (node.nextSibling &&
28208                     node.nextSibling.nodeType == 1 &&
28209                     node.nextSibling.nodeName == 'BR' )
28210                 {
28211                     text = text.replace(/\s+$/g,'');
28212                 }
28213                 // if previous tag was a BR, we can also trim..
28214                 if (node.previousSibling &&
28215                     node.previousSibling.nodeType == 1 &&
28216                     node.previousSibling.nodeName == 'BR' )
28217                 {
28218                     text = this.indentstr +  text.replace(/^\s+/g,'');
28219                 }
28220                 if (text.match(/\n/)) {
28221                     text = text.replace(
28222                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28223                     );
28224                     // remoeve the last whitespace / line break.
28225                     text = text.replace(/\n\s+$/,'');
28226                 }
28227                 // repace long lines
28228                 
28229             }
28230              
28231             this.html[this.html.length] =  text;
28232             return;   
28233         }
28234         // see if previous element was a inline element.
28235         var indentstr = this.indentstr;
28236    
28237         text = text.replace(/\s+/g," "); // all whitespace into single white space.
28238         
28239         // should trim left?
28240         if (node.previousSibling &&
28241             node.previousSibling.nodeType == 1 &&
28242             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
28243         {
28244             indentstr = '';
28245             
28246         } else {
28247             this.addLine();
28248             text = text.replace(/^\s+/,''); // trim left
28249           
28250         }
28251         // should trim right?
28252         if (node.nextSibling &&
28253             node.nextSibling.nodeType == 1 &&
28254             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
28255         {
28256           // noop
28257             
28258         }  else {
28259             text = text.replace(/\s+$/,''); // trim right
28260         }
28261          
28262               
28263         
28264         
28265         
28266         if (text.length < 1) {
28267             return;
28268         }
28269         if (!text.match(/\n/)) {
28270             this.html.push(indentstr + text);
28271             return;
28272         }
28273         
28274         text = this.indentstr + text.replace(
28275             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28276         );
28277         // remoeve the last whitespace / line break.
28278         text = text.replace(/\s+$/,''); 
28279         
28280         this.html.push(text);
28281         
28282         // split and indent..
28283         
28284         
28285     },
28286     /**
28287      * Writes a cdata node such as <![CDATA[data]]>.
28288      *
28289      * @method cdata
28290      * @param {String} text String to write out inside the cdata.
28291      */
28292     cdata: function(text) {
28293         this.html.push('<![CDATA[', text, ']]>');
28294     },
28295     /**
28296     * Writes a comment node such as <!-- Comment -->.
28297     *
28298     * @method cdata
28299     * @param {String} text String to write out inside the comment.
28300     */
28301    comment: function(text) {
28302        this.html.push('<!--', text, '-->');
28303    },
28304     /**
28305      * Writes a PI node such as <?xml attr="value" ?>.
28306      *
28307      * @method pi
28308      * @param {String} name Name of the pi.
28309      * @param {String} text String to write out inside the pi.
28310      */
28311     pi: function(name, text) {
28312         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28313         this.indent != '' && this.html.push('\n');
28314     },
28315     /**
28316      * Writes a doctype node such as <!DOCTYPE data>.
28317      *
28318      * @method doctype
28319      * @param {String} text String to write out inside the doctype.
28320      */
28321     doctype: function(text) {
28322         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28323     },
28324     /**
28325      * Resets the internal buffer if one wants to reuse the writer.
28326      *
28327      * @method reset
28328      */
28329     reset: function() {
28330         this.html.length = 0;
28331         this.state = [];
28332         this.pushState({
28333             indentstr : '',
28334             in_pre : false, 
28335             in_inline : false
28336         })
28337     },
28338     /**
28339      * Returns the contents that got serialized.
28340      *
28341      * @method getContent
28342      * @return {String} HTML contents that got written down.
28343      */
28344     getContent: function() {
28345         return this.html.join('').replace(/\n$/, '');
28346     },
28347     
28348     pushState : function(cfg)
28349     {
28350         this.state.push(cfg);
28351         Roo.apply(this, cfg);
28352     },
28353     
28354     popState : function()
28355     {
28356         if (this.state.length < 1) {
28357             return; // nothing to push
28358         }
28359         var cfg = {
28360             in_pre: false,
28361             indentstr : ''
28362         };
28363         this.state.pop();
28364         if (this.state.length > 0) {
28365             cfg = this.state[this.state.length-1]; 
28366         }
28367         Roo.apply(this, cfg);
28368     },
28369     
28370     addLine: function()
28371     {
28372         if (this.html.length < 1) {
28373             return;
28374         }
28375         
28376         
28377         var value = this.html[this.html.length - 1];
28378         if (value.length > 0 && '\n' !== value) {
28379             this.html.push('\n');
28380         }
28381     }
28382     
28383     
28384 //'pre script noscript style textarea video audio iframe object code'
28385 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28386 // inline 
28387 };
28388
28389 Roo.htmleditor.TidyWriter.inline_elements = [
28390         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28391         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28392 ];
28393 Roo.htmleditor.TidyWriter.shortend_elements = [
28394     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28395     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28396 ];
28397
28398 Roo.htmleditor.TidyWriter.whitespace_elements = [
28399     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28400 ];/***
28401  * This is based loosely on tinymce 
28402  * @class Roo.htmleditor.TidyEntities
28403  * @static
28404  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28405  *
28406  * Not 100% sure this is actually used or needed.
28407  */
28408
28409 Roo.htmleditor.TidyEntities = {
28410     
28411     /**
28412      * initialize data..
28413      */
28414     init : function (){
28415      
28416         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28417        
28418     },
28419
28420
28421     buildEntitiesLookup: function(items, radix) {
28422         var i, chr, entity, lookup = {};
28423         if (!items) {
28424             return {};
28425         }
28426         items = typeof(items) == 'string' ? items.split(',') : items;
28427         radix = radix || 10;
28428         // Build entities lookup table
28429         for (i = 0; i < items.length; i += 2) {
28430             chr = String.fromCharCode(parseInt(items[i], radix));
28431             // Only add non base entities
28432             if (!this.baseEntities[chr]) {
28433                 entity = '&' + items[i + 1] + ';';
28434                 lookup[chr] = entity;
28435                 lookup[entity] = chr;
28436             }
28437         }
28438         return lookup;
28439         
28440     },
28441     
28442     asciiMap : {
28443             128: '€',
28444             130: '‚',
28445             131: 'ƒ',
28446             132: '„',
28447             133: '…',
28448             134: '†',
28449             135: '‡',
28450             136: 'ˆ',
28451             137: '‰',
28452             138: 'Š',
28453             139: '‹',
28454             140: 'Œ',
28455             142: 'Ž',
28456             145: '‘',
28457             146: '’',
28458             147: '“',
28459             148: '”',
28460             149: '•',
28461             150: '–',
28462             151: '—',
28463             152: '˜',
28464             153: '™',
28465             154: 'š',
28466             155: '›',
28467             156: 'œ',
28468             158: 'ž',
28469             159: 'Ÿ'
28470     },
28471     // Raw entities
28472     baseEntities : {
28473         '"': '&quot;',
28474         // Needs to be escaped since the YUI compressor would otherwise break the code
28475         '\'': '&#39;',
28476         '<': '&lt;',
28477         '>': '&gt;',
28478         '&': '&amp;',
28479         '`': '&#96;'
28480     },
28481     // Reverse lookup table for raw entities
28482     reverseEntities : {
28483         '&lt;': '<',
28484         '&gt;': '>',
28485         '&amp;': '&',
28486         '&quot;': '"',
28487         '&apos;': '\''
28488     },
28489     
28490     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28491     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28492     rawCharsRegExp : /[<>&\"\']/g,
28493     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28494     namedEntities  : false,
28495     namedEntitiesData : [ 
28496         '50',
28497         'nbsp',
28498         '51',
28499         'iexcl',
28500         '52',
28501         'cent',
28502         '53',
28503         'pound',
28504         '54',
28505         'curren',
28506         '55',
28507         'yen',
28508         '56',
28509         'brvbar',
28510         '57',
28511         'sect',
28512         '58',
28513         'uml',
28514         '59',
28515         'copy',
28516         '5a',
28517         'ordf',
28518         '5b',
28519         'laquo',
28520         '5c',
28521         'not',
28522         '5d',
28523         'shy',
28524         '5e',
28525         'reg',
28526         '5f',
28527         'macr',
28528         '5g',
28529         'deg',
28530         '5h',
28531         'plusmn',
28532         '5i',
28533         'sup2',
28534         '5j',
28535         'sup3',
28536         '5k',
28537         'acute',
28538         '5l',
28539         'micro',
28540         '5m',
28541         'para',
28542         '5n',
28543         'middot',
28544         '5o',
28545         'cedil',
28546         '5p',
28547         'sup1',
28548         '5q',
28549         'ordm',
28550         '5r',
28551         'raquo',
28552         '5s',
28553         'frac14',
28554         '5t',
28555         'frac12',
28556         '5u',
28557         'frac34',
28558         '5v',
28559         'iquest',
28560         '60',
28561         'Agrave',
28562         '61',
28563         'Aacute',
28564         '62',
28565         'Acirc',
28566         '63',
28567         'Atilde',
28568         '64',
28569         'Auml',
28570         '65',
28571         'Aring',
28572         '66',
28573         'AElig',
28574         '67',
28575         'Ccedil',
28576         '68',
28577         'Egrave',
28578         '69',
28579         'Eacute',
28580         '6a',
28581         'Ecirc',
28582         '6b',
28583         'Euml',
28584         '6c',
28585         'Igrave',
28586         '6d',
28587         'Iacute',
28588         '6e',
28589         'Icirc',
28590         '6f',
28591         'Iuml',
28592         '6g',
28593         'ETH',
28594         '6h',
28595         'Ntilde',
28596         '6i',
28597         'Ograve',
28598         '6j',
28599         'Oacute',
28600         '6k',
28601         'Ocirc',
28602         '6l',
28603         'Otilde',
28604         '6m',
28605         'Ouml',
28606         '6n',
28607         'times',
28608         '6o',
28609         'Oslash',
28610         '6p',
28611         'Ugrave',
28612         '6q',
28613         'Uacute',
28614         '6r',
28615         'Ucirc',
28616         '6s',
28617         'Uuml',
28618         '6t',
28619         'Yacute',
28620         '6u',
28621         'THORN',
28622         '6v',
28623         'szlig',
28624         '70',
28625         'agrave',
28626         '71',
28627         'aacute',
28628         '72',
28629         'acirc',
28630         '73',
28631         'atilde',
28632         '74',
28633         'auml',
28634         '75',
28635         'aring',
28636         '76',
28637         'aelig',
28638         '77',
28639         'ccedil',
28640         '78',
28641         'egrave',
28642         '79',
28643         'eacute',
28644         '7a',
28645         'ecirc',
28646         '7b',
28647         'euml',
28648         '7c',
28649         'igrave',
28650         '7d',
28651         'iacute',
28652         '7e',
28653         'icirc',
28654         '7f',
28655         'iuml',
28656         '7g',
28657         'eth',
28658         '7h',
28659         'ntilde',
28660         '7i',
28661         'ograve',
28662         '7j',
28663         'oacute',
28664         '7k',
28665         'ocirc',
28666         '7l',
28667         'otilde',
28668         '7m',
28669         'ouml',
28670         '7n',
28671         'divide',
28672         '7o',
28673         'oslash',
28674         '7p',
28675         'ugrave',
28676         '7q',
28677         'uacute',
28678         '7r',
28679         'ucirc',
28680         '7s',
28681         'uuml',
28682         '7t',
28683         'yacute',
28684         '7u',
28685         'thorn',
28686         '7v',
28687         'yuml',
28688         'ci',
28689         'fnof',
28690         'sh',
28691         'Alpha',
28692         'si',
28693         'Beta',
28694         'sj',
28695         'Gamma',
28696         'sk',
28697         'Delta',
28698         'sl',
28699         'Epsilon',
28700         'sm',
28701         'Zeta',
28702         'sn',
28703         'Eta',
28704         'so',
28705         'Theta',
28706         'sp',
28707         'Iota',
28708         'sq',
28709         'Kappa',
28710         'sr',
28711         'Lambda',
28712         'ss',
28713         'Mu',
28714         'st',
28715         'Nu',
28716         'su',
28717         'Xi',
28718         'sv',
28719         'Omicron',
28720         't0',
28721         'Pi',
28722         't1',
28723         'Rho',
28724         't3',
28725         'Sigma',
28726         't4',
28727         'Tau',
28728         't5',
28729         'Upsilon',
28730         't6',
28731         'Phi',
28732         't7',
28733         'Chi',
28734         't8',
28735         'Psi',
28736         't9',
28737         'Omega',
28738         'th',
28739         'alpha',
28740         'ti',
28741         'beta',
28742         'tj',
28743         'gamma',
28744         'tk',
28745         'delta',
28746         'tl',
28747         'epsilon',
28748         'tm',
28749         'zeta',
28750         'tn',
28751         'eta',
28752         'to',
28753         'theta',
28754         'tp',
28755         'iota',
28756         'tq',
28757         'kappa',
28758         'tr',
28759         'lambda',
28760         'ts',
28761         'mu',
28762         'tt',
28763         'nu',
28764         'tu',
28765         'xi',
28766         'tv',
28767         'omicron',
28768         'u0',
28769         'pi',
28770         'u1',
28771         'rho',
28772         'u2',
28773         'sigmaf',
28774         'u3',
28775         'sigma',
28776         'u4',
28777         'tau',
28778         'u5',
28779         'upsilon',
28780         'u6',
28781         'phi',
28782         'u7',
28783         'chi',
28784         'u8',
28785         'psi',
28786         'u9',
28787         'omega',
28788         'uh',
28789         'thetasym',
28790         'ui',
28791         'upsih',
28792         'um',
28793         'piv',
28794         '812',
28795         'bull',
28796         '816',
28797         'hellip',
28798         '81i',
28799         'prime',
28800         '81j',
28801         'Prime',
28802         '81u',
28803         'oline',
28804         '824',
28805         'frasl',
28806         '88o',
28807         'weierp',
28808         '88h',
28809         'image',
28810         '88s',
28811         'real',
28812         '892',
28813         'trade',
28814         '89l',
28815         'alefsym',
28816         '8cg',
28817         'larr',
28818         '8ch',
28819         'uarr',
28820         '8ci',
28821         'rarr',
28822         '8cj',
28823         'darr',
28824         '8ck',
28825         'harr',
28826         '8dl',
28827         'crarr',
28828         '8eg',
28829         'lArr',
28830         '8eh',
28831         'uArr',
28832         '8ei',
28833         'rArr',
28834         '8ej',
28835         'dArr',
28836         '8ek',
28837         'hArr',
28838         '8g0',
28839         'forall',
28840         '8g2',
28841         'part',
28842         '8g3',
28843         'exist',
28844         '8g5',
28845         'empty',
28846         '8g7',
28847         'nabla',
28848         '8g8',
28849         'isin',
28850         '8g9',
28851         'notin',
28852         '8gb',
28853         'ni',
28854         '8gf',
28855         'prod',
28856         '8gh',
28857         'sum',
28858         '8gi',
28859         'minus',
28860         '8gn',
28861         'lowast',
28862         '8gq',
28863         'radic',
28864         '8gt',
28865         'prop',
28866         '8gu',
28867         'infin',
28868         '8h0',
28869         'ang',
28870         '8h7',
28871         'and',
28872         '8h8',
28873         'or',
28874         '8h9',
28875         'cap',
28876         '8ha',
28877         'cup',
28878         '8hb',
28879         'int',
28880         '8hk',
28881         'there4',
28882         '8hs',
28883         'sim',
28884         '8i5',
28885         'cong',
28886         '8i8',
28887         'asymp',
28888         '8j0',
28889         'ne',
28890         '8j1',
28891         'equiv',
28892         '8j4',
28893         'le',
28894         '8j5',
28895         'ge',
28896         '8k2',
28897         'sub',
28898         '8k3',
28899         'sup',
28900         '8k4',
28901         'nsub',
28902         '8k6',
28903         'sube',
28904         '8k7',
28905         'supe',
28906         '8kl',
28907         'oplus',
28908         '8kn',
28909         'otimes',
28910         '8l5',
28911         'perp',
28912         '8m5',
28913         'sdot',
28914         '8o8',
28915         'lceil',
28916         '8o9',
28917         'rceil',
28918         '8oa',
28919         'lfloor',
28920         '8ob',
28921         'rfloor',
28922         '8p9',
28923         'lang',
28924         '8pa',
28925         'rang',
28926         '9ea',
28927         'loz',
28928         '9j0',
28929         'spades',
28930         '9j3',
28931         'clubs',
28932         '9j5',
28933         'hearts',
28934         '9j6',
28935         'diams',
28936         'ai',
28937         'OElig',
28938         'aj',
28939         'oelig',
28940         'b0',
28941         'Scaron',
28942         'b1',
28943         'scaron',
28944         'bo',
28945         'Yuml',
28946         'm6',
28947         'circ',
28948         'ms',
28949         'tilde',
28950         '802',
28951         'ensp',
28952         '803',
28953         'emsp',
28954         '809',
28955         'thinsp',
28956         '80c',
28957         'zwnj',
28958         '80d',
28959         'zwj',
28960         '80e',
28961         'lrm',
28962         '80f',
28963         'rlm',
28964         '80j',
28965         'ndash',
28966         '80k',
28967         'mdash',
28968         '80o',
28969         'lsquo',
28970         '80p',
28971         'rsquo',
28972         '80q',
28973         'sbquo',
28974         '80s',
28975         'ldquo',
28976         '80t',
28977         'rdquo',
28978         '80u',
28979         'bdquo',
28980         '810',
28981         'dagger',
28982         '811',
28983         'Dagger',
28984         '81g',
28985         'permil',
28986         '81p',
28987         'lsaquo',
28988         '81q',
28989         'rsaquo',
28990         '85c',
28991         'euro'
28992     ],
28993
28994          
28995     /**
28996      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28997      *
28998      * @method encodeRaw
28999      * @param {String} text Text to encode.
29000      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
29001      * @return {String} Entity encoded text.
29002      */
29003     encodeRaw: function(text, attr)
29004     {
29005         var t = this;
29006         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
29007             return t.baseEntities[chr] || chr;
29008         });
29009     },
29010     /**
29011      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
29012      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
29013      * and is exposed as the DOMUtils.encode function.
29014      *
29015      * @method encodeAllRaw
29016      * @param {String} text Text to encode.
29017      * @return {String} Entity encoded text.
29018      */
29019     encodeAllRaw: function(text) {
29020         var t = this;
29021         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
29022             return t.baseEntities[chr] || chr;
29023         });
29024     },
29025     /**
29026      * Encodes the specified string using numeric entities. The core entities will be
29027      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
29028      *
29029      * @method encodeNumeric
29030      * @param {String} text Text to encode.
29031      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
29032      * @return {String} Entity encoded text.
29033      */
29034     encodeNumeric: function(text, attr) {
29035         var t = this;
29036         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
29037             // Multi byte sequence convert it to a single entity
29038             if (chr.length > 1) {
29039                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
29040             }
29041             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
29042         });
29043     },
29044     /**
29045      * Encodes the specified string using named entities. The core entities will be encoded
29046      * as named ones but all non lower ascii characters will be encoded into named entities.
29047      *
29048      * @method encodeNamed
29049      * @param {String} text Text to encode.
29050      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
29051      * @param {Object} entities Optional parameter with entities to use.
29052      * @return {String} Entity encoded text.
29053      */
29054     encodeNamed: function(text, attr, entities) {
29055         var t = this;
29056         entities = entities || this.namedEntities;
29057         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
29058             return t.baseEntities[chr] || entities[chr] || chr;
29059         });
29060     },
29061     /**
29062      * Returns an encode function based on the name(s) and it's optional entities.
29063      *
29064      * @method getEncodeFunc
29065      * @param {String} name Comma separated list of encoders for example named,numeric.
29066      * @param {String} entities Optional parameter with entities to use instead of the built in set.
29067      * @return {function} Encode function to be used.
29068      */
29069     getEncodeFunc: function(name, entities) {
29070         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
29071         var t = this;
29072         function encodeNamedAndNumeric(text, attr) {
29073             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
29074                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
29075             });
29076         }
29077
29078         function encodeCustomNamed(text, attr) {
29079             return t.encodeNamed(text, attr, entities);
29080         }
29081         // Replace + with , to be compatible with previous TinyMCE versions
29082         name = this.makeMap(name.replace(/\+/g, ','));
29083         // Named and numeric encoder
29084         if (name.named && name.numeric) {
29085             return this.encodeNamedAndNumeric;
29086         }
29087         // Named encoder
29088         if (name.named) {
29089             // Custom names
29090             if (entities) {
29091                 return encodeCustomNamed;
29092             }
29093             return this.encodeNamed;
29094         }
29095         // Numeric
29096         if (name.numeric) {
29097             return this.encodeNumeric;
29098         }
29099         // Raw encoder
29100         return this.encodeRaw;
29101     },
29102     /**
29103      * Decodes the specified string, this will replace entities with raw UTF characters.
29104      *
29105      * @method decode
29106      * @param {String} text Text to entity decode.
29107      * @return {String} Entity decoded string.
29108      */
29109     decode: function(text)
29110     {
29111         var  t = this;
29112         return text.replace(this.entityRegExp, function(all, numeric) {
29113             if (numeric) {
29114                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
29115                 // Support upper UTF
29116                 if (numeric > 65535) {
29117                     numeric -= 65536;
29118                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
29119                 }
29120                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
29121             }
29122             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
29123         });
29124     },
29125     nativeDecode : function (text) {
29126         return text;
29127     },
29128     makeMap : function (items, delim, map) {
29129                 var i;
29130                 items = items || [];
29131                 delim = delim || ',';
29132                 if (typeof items == "string") {
29133                         items = items.split(delim);
29134                 }
29135                 map = map || {};
29136                 i = items.length;
29137                 while (i--) {
29138                         map[items[i]] = {};
29139                 }
29140                 return map;
29141         }
29142 };
29143     
29144     
29145     
29146 Roo.htmleditor.TidyEntities.init();
29147 /**
29148  * @class Roo.htmleditor.KeyEnter
29149  * Handle Enter press..
29150  * @cfg {Roo.HtmlEditorCore} core the editor.
29151  * @constructor
29152  * Create a new Filter.
29153  * @param {Object} config Configuration options
29154  */
29155
29156
29157
29158
29159
29160 Roo.htmleditor.KeyEnter = function(cfg) {
29161     Roo.apply(this, cfg);
29162     // this does not actually call walk as it's really just a abstract class
29163  
29164     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
29165 }
29166
29167 //Roo.htmleditor.KeyEnter.i = 0;
29168
29169
29170 Roo.htmleditor.KeyEnter.prototype = {
29171     
29172     core : false,
29173     
29174     keypress : function(e)
29175     {
29176         if (e.charCode != 13 && e.charCode != 10) {
29177             Roo.log([e.charCode,e]);
29178             return true;
29179         }
29180         e.preventDefault();
29181         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
29182         var doc = this.core.doc;
29183           //add a new line
29184        
29185     
29186         var sel = this.core.getSelection();
29187         var range = sel.getRangeAt(0);
29188         var n = range.commonAncestorContainer;
29189         var pc = range.closest([ 'ol', 'ul']);
29190         var pli = range.closest('li');
29191         if (!pc || e.ctrlKey) {
29192             // on it list, or ctrl pressed.
29193             if (!e.ctrlKey) {
29194                 sel.insertNode('br', 'after'); 
29195             } else {
29196                 // only do this if we have ctrl key..
29197                 var br = doc.createElement('br');
29198                 br.className = 'clear';
29199                 br.setAttribute('style', 'clear: both');
29200                 sel.insertNode(br, 'after'); 
29201             }
29202             
29203          
29204             this.core.undoManager.addEvent();
29205             this.core.fireEditorEvent(e);
29206             return false;
29207         }
29208         
29209         // deal with <li> insetion
29210         if (pli.innerText.trim() == '' &&
29211             pli.previousSibling &&
29212             pli.previousSibling.nodeName == 'LI' &&
29213             pli.previousSibling.innerText.trim() ==  '') {
29214             pli.parentNode.removeChild(pli.previousSibling);
29215             sel.cursorAfter(pc);
29216             this.core.undoManager.addEvent();
29217             this.core.fireEditorEvent(e);
29218             return false;
29219         }
29220     
29221         var li = doc.createElement('LI');
29222         li.innerHTML = '&nbsp;';
29223         if (!pli || !pli.firstSibling) {
29224             pc.appendChild(li);
29225         } else {
29226             pli.parentNode.insertBefore(li, pli.firstSibling);
29227         }
29228         sel.cursorText (li.firstChild);
29229       
29230         this.core.undoManager.addEvent();
29231         this.core.fireEditorEvent(e);
29232
29233         return false;
29234         
29235     
29236         
29237         
29238          
29239     }
29240 };
29241      
29242 /**
29243  * @class Roo.htmleditor.Block
29244  * Base class for html editor blocks - do not use it directly .. extend it..
29245  * @cfg {DomElement} node The node to apply stuff to.
29246  * @cfg {String} friendly_name the name that appears in the context bar about this block
29247  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
29248  
29249  * @constructor
29250  * Create a new Filter.
29251  * @param {Object} config Configuration options
29252  */
29253
29254 Roo.htmleditor.Block  = function(cfg)
29255 {
29256     // do nothing .. should not be called really.
29257 }
29258 /**
29259  * factory method to get the block from an element (using cache if necessary)
29260  * @static
29261  * @param {HtmlElement} the dom element
29262  */
29263 Roo.htmleditor.Block.factory = function(node)
29264 {
29265     var cc = Roo.htmleditor.Block.cache;
29266     var id = Roo.get(node).id;
29267     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
29268         Roo.htmleditor.Block.cache[id].readElement(node);
29269         return Roo.htmleditor.Block.cache[id];
29270     }
29271     var db  = node.getAttribute('data-block');
29272     if (!db) {
29273         db = node.nodeName.toLowerCase().toUpperCaseFirst();
29274     }
29275     var cls = Roo.htmleditor['Block' + db];
29276     if (typeof(cls) == 'undefined') {
29277         //Roo.log(node.getAttribute('data-block'));
29278         Roo.log("OOps missing block : " + 'Block' + db);
29279         return false;
29280     }
29281     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29282     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
29283 };
29284
29285 /**
29286  * initalize all Elements from content that are 'blockable'
29287  * @static
29288  * @param the body element
29289  */
29290 Roo.htmleditor.Block.initAll = function(body, type)
29291 {
29292     if (typeof(type) == 'undefined') {
29293         var ia = Roo.htmleditor.Block.initAll;
29294         ia(body,'table');
29295         ia(body,'td');
29296         ia(body,'figure');
29297         return;
29298     }
29299     Roo.each(Roo.get(body).query(type), function(e) {
29300         Roo.htmleditor.Block.factory(e);    
29301     },this);
29302 };
29303 // question goes here... do we need to clear out this cache sometimes?
29304 // or show we make it relivant to the htmleditor.
29305 Roo.htmleditor.Block.cache = {};
29306
29307 Roo.htmleditor.Block.prototype = {
29308     
29309     node : false,
29310     
29311      // used by context menu
29312     friendly_name : 'Based Block',
29313     
29314     // text for button to delete this element
29315     deleteTitle : false,
29316     
29317     context : false,
29318     /**
29319      * Update a node with values from this object
29320      * @param {DomElement} node
29321      */
29322     updateElement : function(node)
29323     {
29324         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29325     },
29326      /**
29327      * convert to plain HTML for calling insertAtCursor..
29328      */
29329     toHTML : function()
29330     {
29331         return Roo.DomHelper.markup(this.toObject());
29332     },
29333     /**
29334      * used by readEleemnt to extract data from a node
29335      * may need improving as it's pretty basic
29336      
29337      * @param {DomElement} node
29338      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29339      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29340      * @param {String} style the style property - eg. text-align
29341      */
29342     getVal : function(node, tag, attr, style)
29343     {
29344         var n = node;
29345         if (tag !== true && n.tagName != tag.toUpperCase()) {
29346             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29347             // but kiss for now.
29348             n = node.getElementsByTagName(tag).item(0);
29349         }
29350         if (!n) {
29351             return '';
29352         }
29353         if (attr === false) {
29354             return n;
29355         }
29356         if (attr == 'html') {
29357             return n.innerHTML;
29358         }
29359         if (attr == 'style') {
29360             return n.style[style]; 
29361         }
29362         
29363         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29364             
29365     },
29366     /**
29367      * create a DomHelper friendly object - for use with 
29368      * Roo.DomHelper.markup / overwrite / etc..
29369      * (override this)
29370      */
29371     toObject : function()
29372     {
29373         return {};
29374     },
29375       /**
29376      * Read a node that has a 'data-block' property - and extract the values from it.
29377      * @param {DomElement} node - the node
29378      */
29379     readElement : function(node)
29380     {
29381         
29382     } 
29383     
29384     
29385 };
29386
29387  
29388
29389 /**
29390  * @class Roo.htmleditor.BlockFigure
29391  * Block that has an image and a figcaption
29392  * @cfg {String} image_src the url for the image
29393  * @cfg {String} align (left|right) alignment for the block default left
29394  * @cfg {String} caption the text to appear below  (and in the alt tag)
29395  * @cfg {String} caption_display (block|none) display or not the caption
29396  * @cfg {String|number} image_width the width of the image number or %?
29397  * @cfg {String|number} image_height the height of the image number or %?
29398  * 
29399  * @constructor
29400  * Create a new Filter.
29401  * @param {Object} config Configuration options
29402  */
29403
29404 Roo.htmleditor.BlockFigure = function(cfg)
29405 {
29406     if (cfg.node) {
29407         this.readElement(cfg.node);
29408         this.updateElement(cfg.node);
29409     }
29410     Roo.apply(this, cfg);
29411 }
29412 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29413  
29414     
29415     // setable values.
29416     image_src: '',
29417     align: 'center',
29418     caption : '',
29419     caption_display : 'block',
29420     width : '100%',
29421     cls : '',
29422     href: '',
29423     video_url : '',
29424     
29425     // margin: '2%', not used
29426     
29427     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29428
29429     
29430     // used by context menu
29431     friendly_name : 'Image with caption',
29432     deleteTitle : "Delete Image and Caption",
29433     
29434     contextMenu : function(toolbar)
29435     {
29436         
29437         var block = function() {
29438             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29439         };
29440         
29441         
29442         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29443         
29444         var syncValue = toolbar.editorcore.syncValue;
29445         
29446         var fields = {};
29447         
29448         return [
29449              {
29450                 xtype : 'TextItem',
29451                 text : "Source: ",
29452                 xns : rooui.Toolbar  //Boostrap?
29453             },
29454             {
29455                 xtype : 'Button',
29456                 text: 'Change Image URL',
29457                  
29458                 listeners : {
29459                     click: function (btn, state)
29460                     {
29461                         var b = block();
29462                         
29463                         Roo.MessageBox.show({
29464                             title : "Image Source URL",
29465                             msg : "Enter the url for the image",
29466                             buttons: Roo.MessageBox.OKCANCEL,
29467                             fn: function(btn, val){
29468                                 if (btn != 'ok') {
29469                                     return;
29470                                 }
29471                                 b.image_src = val;
29472                                 b.updateElement();
29473                                 syncValue();
29474                                 toolbar.editorcore.onEditorEvent();
29475                             },
29476                             minWidth:250,
29477                             prompt:true,
29478                             //multiline: multiline,
29479                             modal : true,
29480                             value : b.image_src
29481                         });
29482                     }
29483                 },
29484                 xns : rooui.Toolbar
29485             },
29486          
29487             {
29488                 xtype : 'Button',
29489                 text: 'Change Link URL',
29490                  
29491                 listeners : {
29492                     click: function (btn, state)
29493                     {
29494                         var b = block();
29495                         
29496                         Roo.MessageBox.show({
29497                             title : "Link URL",
29498                             msg : "Enter the url for the link - leave blank to have no link",
29499                             buttons: Roo.MessageBox.OKCANCEL,
29500                             fn: function(btn, val){
29501                                 if (btn != 'ok') {
29502                                     return;
29503                                 }
29504                                 b.href = val;
29505                                 b.updateElement();
29506                                 syncValue();
29507                                 toolbar.editorcore.onEditorEvent();
29508                             },
29509                             minWidth:250,
29510                             prompt:true,
29511                             //multiline: multiline,
29512                             modal : true,
29513                             value : b.href
29514                         });
29515                     }
29516                 },
29517                 xns : rooui.Toolbar
29518             },
29519             {
29520                 xtype : 'Button',
29521                 text: 'Show Video URL',
29522                  
29523                 listeners : {
29524                     click: function (btn, state)
29525                     {
29526                         Roo.MessageBox.alert("Video URL",
29527                             block().video_url == '' ? 'This image is not linked ot a video' :
29528                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29529                     }
29530                 },
29531                 xns : rooui.Toolbar
29532             },
29533             
29534             
29535             {
29536                 xtype : 'TextItem',
29537                 text : "Width: ",
29538                 xns : rooui.Toolbar  //Boostrap?
29539             },
29540             {
29541                 xtype : 'ComboBox',
29542                 allowBlank : false,
29543                 displayField : 'val',
29544                 editable : true,
29545                 listWidth : 100,
29546                 triggerAction : 'all',
29547                 typeAhead : true,
29548                 valueField : 'val',
29549                 width : 70,
29550                 name : 'width',
29551                 listeners : {
29552                     select : function (combo, r, index)
29553                     {
29554                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29555                         var b = block();
29556                         b.width = r.get('val');
29557                         b.updateElement();
29558                         syncValue();
29559                         toolbar.editorcore.onEditorEvent();
29560                     }
29561                 },
29562                 xns : rooui.form,
29563                 store : {
29564                     xtype : 'SimpleStore',
29565                     data : [
29566                         ['100%'],
29567                         ['80%'],
29568                         ['50%'],
29569                         ['20%'],
29570                         ['10%']
29571                     ],
29572                     fields : [ 'val'],
29573                     xns : Roo.data
29574                 }
29575             },
29576             {
29577                 xtype : 'TextItem',
29578                 text : "Align: ",
29579                 xns : rooui.Toolbar  //Boostrap?
29580             },
29581             {
29582                 xtype : 'ComboBox',
29583                 allowBlank : false,
29584                 displayField : 'val',
29585                 editable : true,
29586                 listWidth : 100,
29587                 triggerAction : 'all',
29588                 typeAhead : true,
29589                 valueField : 'val',
29590                 width : 70,
29591                 name : 'align',
29592                 listeners : {
29593                     select : function (combo, r, index)
29594                     {
29595                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29596                         var b = block();
29597                         b.align = r.get('val');
29598                         b.updateElement();
29599                         syncValue();
29600                         toolbar.editorcore.onEditorEvent();
29601                     }
29602                 },
29603                 xns : rooui.form,
29604                 store : {
29605                     xtype : 'SimpleStore',
29606                     data : [
29607                         ['left'],
29608                         ['right'],
29609                         ['center']
29610                     ],
29611                     fields : [ 'val'],
29612                     xns : Roo.data
29613                 }
29614             },
29615             
29616               
29617             {
29618                 xtype : 'Button',
29619                 text: 'Hide Caption',
29620                 name : 'caption_display',
29621                 pressed : false,
29622                 enableToggle : true,
29623                 setValue : function(v) {
29624                     // this trigger toggle.
29625                      
29626                     this.setText(v ? "Hide Caption" : "Show Caption");
29627                     this.setPressed(v != 'block');
29628                 },
29629                 listeners : {
29630                     toggle: function (btn, state)
29631                     {
29632                         var b  = block();
29633                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29634                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29635                         b.updateElement();
29636                         syncValue();
29637                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29638                         toolbar.editorcore.onEditorEvent();
29639                     }
29640                 },
29641                 xns : rooui.Toolbar
29642             }
29643         ];
29644         
29645     },
29646     /**
29647      * create a DomHelper friendly object - for use with
29648      * Roo.DomHelper.markup / overwrite / etc..
29649      */
29650     toObject : function()
29651     {
29652         var d = document.createElement('div');
29653         d.innerHTML = this.caption;
29654         
29655         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29656         
29657         var iw = this.align == 'center' ? this.width : '100%';
29658         var img =   {
29659             tag : 'img',
29660             contenteditable : 'false',
29661             src : this.image_src,
29662             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29663             style: {
29664                 width : iw,
29665                 maxWidth : iw + ' !important', // this is not getting rendered?
29666                 margin : m  
29667                 
29668             }
29669         };
29670         /*
29671         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29672                     '<a href="{2}">' + 
29673                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29674                     '</a>' + 
29675                 '</div>',
29676         */
29677                 
29678         if (this.href.length > 0) {
29679             img = {
29680                 tag : 'a',
29681                 href: this.href,
29682                 contenteditable : 'true',
29683                 cn : [
29684                     img
29685                 ]
29686             };
29687         }
29688         
29689         
29690         if (this.video_url.length > 0) {
29691             img = {
29692                 tag : 'div',
29693                 cls : this.cls,
29694                 frameborder : 0,
29695                 allowfullscreen : true,
29696                 width : 420,  // these are for video tricks - that we replace the outer
29697                 height : 315,
29698                 src : this.video_url,
29699                 cn : [
29700                     img
29701                 ]
29702             };
29703         }
29704
29705
29706   
29707         var ret =   {
29708             tag: 'figure',
29709             'data-block' : 'Figure',
29710             'data-width' : this.width,
29711             'data-caption' : this.caption, 
29712             'data-caption-display' : this.caption_display,
29713             contenteditable : 'false',
29714             
29715             style : {
29716                 display: 'block',
29717                 float :  this.align ,
29718                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29719                 width : this.align == 'center' ? '100%' : this.width,
29720                 margin:  '0px',
29721                 padding: this.align == 'center' ? '0' : '0 10px' ,
29722                 textAlign : this.align   // seems to work for email..
29723                 
29724             },
29725             
29726             align : this.align,
29727             cn : [
29728                 img
29729             ]
29730         };
29731
29732         // show figcaption only if caption_display is 'block'
29733         if(this.caption_display == 'block') {
29734             ret['cn'].push({
29735                 tag: 'figcaption',
29736                 style : {
29737                     textAlign : 'left',
29738                     fontSize : '16px',
29739                     lineHeight : '24px',
29740                     display : this.caption_display,
29741                     maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29742                     margin: m,
29743                     width: this.align == 'center' ?  this.width : '100%' 
29744                 
29745                      
29746                 },
29747                 cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29748                 cn : [
29749                     {
29750                         tag: 'div',
29751                         style  : {
29752                             marginTop : '16px',
29753                             textAlign : 'start'
29754                         },
29755                         align: 'left',
29756                         cn : [
29757                             {
29758                                 // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29759                                 tag : 'i',
29760                                 contenteditable : Roo.htmleditor.BlockFigure.caption_edit,
29761                                 html : this.caption.length ? this.caption : "Caption" // fake caption
29762                             }
29763                             
29764                         ]
29765                     }
29766                     
29767                 ]
29768                 
29769             });
29770         }
29771         return ret;
29772          
29773     },
29774     
29775     readElement : function(node)
29776     {
29777         // this should not really come from the link...
29778         this.video_url = this.getVal(node, 'div', 'src');
29779         this.cls = this.getVal(node, 'div', 'class');
29780         this.href = this.getVal(node, 'a', 'href');
29781         
29782         
29783         this.image_src = this.getVal(node, 'img', 'src');
29784          
29785         this.align = this.getVal(node, 'figure', 'align');
29786
29787         // caption display is stored in figure
29788         this.caption_display = this.getVal(node, true, 'data-caption-display');
29789
29790         // backward compatible
29791         // it was stored in figcaption
29792         if(this.caption_display == '') {
29793             this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29794         }
29795
29796         // read caption from figcaption
29797         var figcaption = this.getVal(node, 'figcaption', false);
29798
29799         if (figcaption !== '') {
29800             this.caption = this.getVal(figcaption, 'i', 'html');
29801         }
29802                 
29803
29804         // read caption from data-caption in figure if no caption from figcaption
29805         var dc = this.getVal(node, true, 'data-caption');
29806
29807         if(this.caption_display == 'none' && dc && dc.length){
29808             this.caption = dc;
29809         }
29810
29811         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29812         this.width = this.getVal(node, true, 'data-width');
29813         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29814         
29815     },
29816     removeNode : function()
29817     {
29818         return this.node;
29819     }
29820     
29821   
29822    
29823      
29824     
29825     
29826     
29827     
29828 });
29829
29830 Roo.apply(Roo.htmleditor.BlockFigure, {
29831     caption_edit : true
29832 });
29833
29834  
29835
29836 /**
29837  * @class Roo.htmleditor.BlockTable
29838  * Block that manages a table
29839  * 
29840  * @constructor
29841  * Create a new Filter.
29842  * @param {Object} config Configuration options
29843  */
29844
29845 Roo.htmleditor.BlockTable = function(cfg)
29846 {
29847     if (cfg.node) {
29848         this.readElement(cfg.node);
29849         this.updateElement(cfg.node);
29850     }
29851     Roo.apply(this, cfg);
29852     if (!cfg.node) {
29853         this.rows = [];
29854         for(var r = 0; r < this.no_row; r++) {
29855             this.rows[r] = [];
29856             for(var c = 0; c < this.no_col; c++) {
29857                 this.rows[r][c] = this.emptyCell();
29858             }
29859         }
29860     }
29861     
29862     
29863 }
29864 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29865  
29866     rows : false,
29867     no_col : 1,
29868     no_row : 1,
29869     
29870     
29871     width: '100%',
29872     
29873     // used by context menu
29874     friendly_name : 'Table',
29875     deleteTitle : 'Delete Table',
29876     // context menu is drawn once..
29877     
29878     contextMenu : function(toolbar)
29879     {
29880         
29881         var block = function() {
29882             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29883         };
29884         
29885         
29886         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29887         
29888         var syncValue = toolbar.editorcore.syncValue;
29889         
29890         var fields = {};
29891         
29892         return [
29893             {
29894                 xtype : 'TextItem',
29895                 text : "Width: ",
29896                 xns : rooui.Toolbar  //Boostrap?
29897             },
29898             {
29899                 xtype : 'ComboBox',
29900                 allowBlank : false,
29901                 displayField : 'val',
29902                 editable : true,
29903                 listWidth : 100,
29904                 triggerAction : 'all',
29905                 typeAhead : true,
29906                 valueField : 'val',
29907                 width : 100,
29908                 name : 'width',
29909                 listeners : {
29910                     select : function (combo, r, index)
29911                     {
29912                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29913                         var b = block();
29914                         b.width = r.get('val');
29915                         b.updateElement();
29916                         syncValue();
29917                         toolbar.editorcore.onEditorEvent();
29918                     }
29919                 },
29920                 xns : rooui.form,
29921                 store : {
29922                     xtype : 'SimpleStore',
29923                     data : [
29924                         ['100%'],
29925                         ['auto']
29926                     ],
29927                     fields : [ 'val'],
29928                     xns : Roo.data
29929                 }
29930             },
29931             // -------- Cols
29932             
29933             {
29934                 xtype : 'TextItem',
29935                 text : "Columns: ",
29936                 xns : rooui.Toolbar  //Boostrap?
29937             },
29938          
29939             {
29940                 xtype : 'Button',
29941                 text: '-',
29942                 listeners : {
29943                     click : function (_self, e)
29944                     {
29945                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29946                         block().removeColumn();
29947                         syncValue();
29948                         toolbar.editorcore.onEditorEvent();
29949                     }
29950                 },
29951                 xns : rooui.Toolbar
29952             },
29953             {
29954                 xtype : 'Button',
29955                 text: '+',
29956                 listeners : {
29957                     click : function (_self, e)
29958                     {
29959                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29960                         block().addColumn();
29961                         syncValue();
29962                         toolbar.editorcore.onEditorEvent();
29963                     }
29964                 },
29965                 xns : rooui.Toolbar
29966             },
29967             // -------- ROWS
29968             {
29969                 xtype : 'TextItem',
29970                 text : "Rows: ",
29971                 xns : rooui.Toolbar  //Boostrap?
29972             },
29973          
29974             {
29975                 xtype : 'Button',
29976                 text: '-',
29977                 listeners : {
29978                     click : function (_self, e)
29979                     {
29980                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29981                         block().removeRow();
29982                         syncValue();
29983                         toolbar.editorcore.onEditorEvent();
29984                     }
29985                 },
29986                 xns : rooui.Toolbar
29987             },
29988             {
29989                 xtype : 'Button',
29990                 text: '+',
29991                 listeners : {
29992                     click : function (_self, e)
29993                     {
29994                         block().addRow();
29995                         syncValue();
29996                         toolbar.editorcore.onEditorEvent();
29997                     }
29998                 },
29999                 xns : rooui.Toolbar
30000             },
30001             // -------- ROWS
30002             {
30003                 xtype : 'Button',
30004                 text: 'Reset Column Widths',
30005                 listeners : {
30006                     
30007                     click : function (_self, e)
30008                     {
30009                         block().resetWidths();
30010                         syncValue();
30011                         toolbar.editorcore.onEditorEvent();
30012                     }
30013                 },
30014                 xns : rooui.Toolbar
30015             } 
30016             
30017             
30018             
30019         ];
30020         
30021     },
30022     
30023     
30024   /**
30025      * create a DomHelper friendly object - for use with
30026      * Roo.DomHelper.markup / overwrite / etc..
30027      * ?? should it be called with option to hide all editing features?
30028      */
30029     toObject : function()
30030     {
30031         
30032         var ret = {
30033             tag : 'table',
30034             contenteditable : 'false', // this stops cell selection from picking the table.
30035             'data-block' : 'Table',
30036             style : {
30037                 width:  this.width,
30038                 border : 'solid 1px #000', // ??? hard coded?
30039                 'border-collapse' : 'collapse' 
30040             },
30041             cn : [
30042                 { tag : 'tbody' , cn : [] }
30043             ]
30044         };
30045         
30046         // do we have a head = not really 
30047         var ncols = 0;
30048         Roo.each(this.rows, function( row ) {
30049             var tr = {
30050                 tag: 'tr',
30051                 style : {
30052                     margin: '6px',
30053                     border : 'solid 1px #000',
30054                     textAlign : 'left' 
30055                 },
30056                 cn : [ ]
30057             };
30058             
30059             ret.cn[0].cn.push(tr);
30060             // does the row have any properties? ?? height?
30061             var nc = 0;
30062             Roo.each(row, function( cell ) {
30063                 
30064                 var td = {
30065                     tag : 'td',
30066                     contenteditable :  'true',
30067                     'data-block' : 'Td',
30068                     html : cell.html,
30069                     style : cell.style
30070                 };
30071                 if (cell.colspan > 1) {
30072                     td.colspan = cell.colspan ;
30073                     nc += cell.colspan;
30074                 } else {
30075                     nc++;
30076                 }
30077                 if (cell.rowspan > 1) {
30078                     td.rowspan = cell.rowspan ;
30079                 }
30080                 
30081                 
30082                 // widths ?
30083                 tr.cn.push(td);
30084                     
30085                 
30086             }, this);
30087             ncols = Math.max(nc, ncols);
30088             
30089             
30090         }, this);
30091         // add the header row..
30092         
30093         ncols++;
30094          
30095         
30096         return ret;
30097          
30098     },
30099     
30100     readElement : function(node)
30101     {
30102         node  = node ? node : this.node ;
30103         this.width = this.getVal(node, true, 'style', 'width') || '100%';
30104         
30105         this.rows = [];
30106         this.no_row = 0;
30107         var trs = Array.from(node.rows);
30108         trs.forEach(function(tr) {
30109             var row =  [];
30110             this.rows.push(row);
30111             
30112             this.no_row++;
30113             var no_column = 0;
30114             Array.from(tr.cells).forEach(function(td) {
30115                 
30116                 var add = {
30117                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
30118                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
30119                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
30120                     html : td.innerHTML
30121                 };
30122                 no_column += add.colspan;
30123                      
30124                 
30125                 row.push(add);
30126                 
30127                 
30128             },this);
30129             this.no_col = Math.max(this.no_col, no_column);
30130             
30131             
30132         },this);
30133         
30134         
30135     },
30136     normalizeRows: function()
30137     {
30138         var ret= [];
30139         var rid = -1;
30140         this.rows.forEach(function(row) {
30141             rid++;
30142             ret[rid] = [];
30143             row = this.normalizeRow(row);
30144             var cid = 0;
30145             row.forEach(function(c) {
30146                 while (typeof(ret[rid][cid]) != 'undefined') {
30147                     cid++;
30148                 }
30149                 if (typeof(ret[rid]) == 'undefined') {
30150                     ret[rid] = [];
30151                 }
30152                 ret[rid][cid] = c;
30153                 c.row = rid;
30154                 c.col = cid;
30155                 if (c.rowspan < 2) {
30156                     return;
30157                 }
30158                 
30159                 for(var i = 1 ;i < c.rowspan; i++) {
30160                     if (typeof(ret[rid+i]) == 'undefined') {
30161                         ret[rid+i] = [];
30162                     }
30163                     ret[rid+i][cid] = c;
30164                 }
30165             });
30166         }, this);
30167         return ret;
30168     
30169     },
30170     
30171     normalizeRow: function(row)
30172     {
30173         var ret= [];
30174         row.forEach(function(c) {
30175             if (c.colspan < 2) {
30176                 ret.push(c);
30177                 return;
30178             }
30179             for(var i =0 ;i < c.colspan; i++) {
30180                 ret.push(c);
30181             }
30182         });
30183         return ret;
30184     
30185     },
30186     
30187     deleteColumn : function(sel)
30188     {
30189         if (!sel || sel.type != 'col') {
30190             return;
30191         }
30192         if (this.no_col < 2) {
30193             return;
30194         }
30195         
30196         this.rows.forEach(function(row) {
30197             var cols = this.normalizeRow(row);
30198             var col = cols[sel.col];
30199             if (col.colspan > 1) {
30200                 col.colspan --;
30201             } else {
30202                 row.remove(col);
30203             }
30204             
30205         }, this);
30206         this.no_col--;
30207         
30208     },
30209     removeColumn : function()
30210     {
30211         this.deleteColumn({
30212             type: 'col',
30213             col : this.no_col-1
30214         });
30215         this.updateElement();
30216     },
30217     
30218      
30219     addColumn : function()
30220     {
30221         
30222         this.rows.forEach(function(row) {
30223             row.push(this.emptyCell());
30224            
30225         }, this);
30226         this.updateElement();
30227     },
30228     
30229     deleteRow : function(sel)
30230     {
30231         if (!sel || sel.type != 'row') {
30232             return;
30233         }
30234         
30235         if (this.no_row < 2) {
30236             return;
30237         }
30238         
30239         var rows = this.normalizeRows();
30240         
30241         
30242         rows[sel.row].forEach(function(col) {
30243             if (col.rowspan > 1) {
30244                 col.rowspan--;
30245             } else {
30246                 col.remove = 1; // flage it as removed.
30247             }
30248             
30249         }, this);
30250         var newrows = [];
30251         this.rows.forEach(function(row) {
30252             newrow = [];
30253             row.forEach(function(c) {
30254                 if (typeof(c.remove) == 'undefined') {
30255                     newrow.push(c);
30256                 }
30257                 
30258             });
30259             if (newrow.length > 0) {
30260                 newrows.push(row);
30261             }
30262         });
30263         this.rows =  newrows;
30264         
30265         
30266         
30267         this.no_row--;
30268         this.updateElement();
30269         
30270     },
30271     removeRow : function()
30272     {
30273         this.deleteRow({
30274             type: 'row',
30275             row : this.no_row-1
30276         });
30277         
30278     },
30279     
30280      
30281     addRow : function()
30282     {
30283         
30284         var row = [];
30285         for (var i = 0; i < this.no_col; i++ ) {
30286             
30287             row.push(this.emptyCell());
30288            
30289         }
30290         this.rows.push(row);
30291         this.updateElement();
30292         
30293     },
30294      
30295     // the default cell object... at present...
30296     emptyCell : function() {
30297         return (new Roo.htmleditor.BlockTd({})).toObject();
30298         
30299      
30300     },
30301     
30302     removeNode : function()
30303     {
30304         return this.node;
30305     },
30306     
30307     
30308     
30309     resetWidths : function()
30310     {
30311         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30312             var nn = Roo.htmleditor.Block.factory(n);
30313             nn.width = '';
30314             nn.updateElement(n);
30315         });
30316     }
30317     
30318     
30319     
30320     
30321 })
30322
30323 /**
30324  *
30325  * editing a TD?
30326  *
30327  * since selections really work on the table cell, then editing really should work from there
30328  *
30329  * The original plan was to support merging etc... - but that may not be needed yet..
30330  *
30331  * So this simple version will support:
30332  *   add/remove cols
30333  *   adjust the width +/-
30334  *   reset the width...
30335  *   
30336  *
30337  */
30338
30339
30340  
30341
30342 /**
30343  * @class Roo.htmleditor.BlockTable
30344  * Block that manages a table
30345  * 
30346  * @constructor
30347  * Create a new Filter.
30348  * @param {Object} config Configuration options
30349  */
30350
30351 Roo.htmleditor.BlockTd = function(cfg)
30352 {
30353     if (cfg.node) {
30354         this.readElement(cfg.node);
30355         this.updateElement(cfg.node);
30356     }
30357     Roo.apply(this, cfg);
30358      
30359     
30360     
30361 }
30362 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30363  
30364     node : false,
30365     
30366     width: '',
30367     textAlign : 'left',
30368     valign : 'top',
30369     
30370     colspan : 1,
30371     rowspan : 1,
30372     
30373     
30374     // used by context menu
30375     friendly_name : 'Table Cell',
30376     deleteTitle : false, // use our customer delete
30377     
30378     // context menu is drawn once..
30379     
30380     contextMenu : function(toolbar)
30381     {
30382         
30383         var cell = function() {
30384             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30385         };
30386         
30387         var table = function() {
30388             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30389         };
30390         
30391         var lr = false;
30392         var saveSel = function()
30393         {
30394             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30395         }
30396         var restoreSel = function()
30397         {
30398             if (lr) {
30399                 (function() {
30400                     toolbar.editorcore.focus();
30401                     var cr = toolbar.editorcore.getSelection();
30402                     cr.removeAllRanges();
30403                     cr.addRange(lr);
30404                     toolbar.editorcore.onEditorEvent();
30405                 }).defer(10, this);
30406                 
30407                 
30408             }
30409         }
30410         
30411         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30412         
30413         var syncValue = toolbar.editorcore.syncValue;
30414         
30415         var fields = {};
30416         
30417         return [
30418             {
30419                 xtype : 'Button',
30420                 text : 'Edit Table',
30421                 listeners : {
30422                     click : function() {
30423                         var t = toolbar.tb.selectedNode.closest('table');
30424                         toolbar.editorcore.selectNode(t);
30425                         toolbar.editorcore.onEditorEvent();                        
30426                     }
30427                 }
30428                 
30429             },
30430               
30431            
30432              
30433             {
30434                 xtype : 'TextItem',
30435                 text : "Column Width: ",
30436                  xns : rooui.Toolbar 
30437                
30438             },
30439             {
30440                 xtype : 'Button',
30441                 text: '-',
30442                 listeners : {
30443                     click : function (_self, e)
30444                     {
30445                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30446                         cell().shrinkColumn();
30447                         syncValue();
30448                          toolbar.editorcore.onEditorEvent();
30449                     }
30450                 },
30451                 xns : rooui.Toolbar
30452             },
30453             {
30454                 xtype : 'Button',
30455                 text: '+',
30456                 listeners : {
30457                     click : function (_self, e)
30458                     {
30459                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30460                         cell().growColumn();
30461                         syncValue();
30462                         toolbar.editorcore.onEditorEvent();
30463                     }
30464                 },
30465                 xns : rooui.Toolbar
30466             },
30467             
30468             {
30469                 xtype : 'TextItem',
30470                 text : "Vertical Align: ",
30471                 xns : rooui.Toolbar  //Boostrap?
30472             },
30473             {
30474                 xtype : 'ComboBox',
30475                 allowBlank : false,
30476                 displayField : 'val',
30477                 editable : true,
30478                 listWidth : 100,
30479                 triggerAction : 'all',
30480                 typeAhead : true,
30481                 valueField : 'val',
30482                 width : 100,
30483                 name : 'valign',
30484                 listeners : {
30485                     select : function (combo, r, index)
30486                     {
30487                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30488                         var b = cell();
30489                         b.valign = r.get('val');
30490                         b.updateElement();
30491                         syncValue();
30492                         toolbar.editorcore.onEditorEvent();
30493                     }
30494                 },
30495                 xns : rooui.form,
30496                 store : {
30497                     xtype : 'SimpleStore',
30498                     data : [
30499                         ['top'],
30500                         ['middle'],
30501                         ['bottom'] // there are afew more... 
30502                     ],
30503                     fields : [ 'val'],
30504                     xns : Roo.data
30505                 }
30506             },
30507             
30508             {
30509                 xtype : 'TextItem',
30510                 text : "Merge Cells: ",
30511                  xns : rooui.Toolbar 
30512                
30513             },
30514             
30515             
30516             {
30517                 xtype : 'Button',
30518                 text: 'Right',
30519                 listeners : {
30520                     click : function (_self, e)
30521                     {
30522                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30523                         cell().mergeRight();
30524                         //block().growColumn();
30525                         syncValue();
30526                         toolbar.editorcore.onEditorEvent();
30527                     }
30528                 },
30529                 xns : rooui.Toolbar
30530             },
30531              
30532             {
30533                 xtype : 'Button',
30534                 text: 'Below',
30535                 listeners : {
30536                     click : function (_self, e)
30537                     {
30538                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30539                         cell().mergeBelow();
30540                         //block().growColumn();
30541                         syncValue();
30542                         toolbar.editorcore.onEditorEvent();
30543                     }
30544                 },
30545                 xns : rooui.Toolbar
30546             },
30547             {
30548                 xtype : 'TextItem',
30549                 text : "| ",
30550                  xns : rooui.Toolbar 
30551                
30552             },
30553             
30554             {
30555                 xtype : 'Button',
30556                 text: 'Split',
30557                 listeners : {
30558                     click : function (_self, e)
30559                     {
30560                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30561                         cell().split();
30562                         syncValue();
30563                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30564                         toolbar.editorcore.onEditorEvent();
30565                                              
30566                     }
30567                 },
30568                 xns : rooui.Toolbar
30569             },
30570             {
30571                 xtype : 'Fill',
30572                 xns : rooui.Toolbar 
30573                
30574             },
30575         
30576           
30577             {
30578                 xtype : 'Button',
30579                 text: 'Delete',
30580                  
30581                 xns : rooui.Toolbar,
30582                 menu : {
30583                     xtype : 'Menu',
30584                     xns : rooui.menu,
30585                     items : [
30586                         {
30587                             xtype : 'Item',
30588                             html: 'Column',
30589                             listeners : {
30590                                 click : function (_self, e)
30591                                 {
30592                                     var t = table();
30593                                     
30594                                     cell().deleteColumn();
30595                                     syncValue();
30596                                     toolbar.editorcore.selectNode(t.node);
30597                                     toolbar.editorcore.onEditorEvent();   
30598                                 }
30599                             },
30600                             xns : rooui.menu
30601                         },
30602                         {
30603                             xtype : 'Item',
30604                             html: 'Row',
30605                             listeners : {
30606                                 click : function (_self, e)
30607                                 {
30608                                     var t = table();
30609                                     cell().deleteRow();
30610                                     syncValue();
30611                                     
30612                                     toolbar.editorcore.selectNode(t.node);
30613                                     toolbar.editorcore.onEditorEvent();   
30614                                                          
30615                                 }
30616                             },
30617                             xns : rooui.menu
30618                         },
30619                        {
30620                             xtype : 'Separator',
30621                             xns : rooui.menu
30622                         },
30623                         {
30624                             xtype : 'Item',
30625                             html: 'Table',
30626                             listeners : {
30627                                 click : function (_self, e)
30628                                 {
30629                                     var t = table();
30630                                     var nn = t.node.nextSibling || t.node.previousSibling;
30631                                     t.node.parentNode.removeChild(t.node);
30632                                     if (nn) { 
30633                                         toolbar.editorcore.selectNode(nn, true);
30634                                     }
30635                                     toolbar.editorcore.onEditorEvent();   
30636                                                          
30637                                 }
30638                             },
30639                             xns : rooui.menu
30640                         }
30641                     ]
30642                 }
30643             }
30644             
30645             // align... << fixme
30646             
30647         ];
30648         
30649     },
30650     
30651     
30652   /**
30653      * create a DomHelper friendly object - for use with
30654      * Roo.DomHelper.markup / overwrite / etc..
30655      * ?? should it be called with option to hide all editing features?
30656      */
30657  /**
30658      * create a DomHelper friendly object - for use with
30659      * Roo.DomHelper.markup / overwrite / etc..
30660      * ?? should it be called with option to hide all editing features?
30661      */
30662     toObject : function()
30663     {
30664         var ret = {
30665             tag : 'td',
30666             contenteditable : 'true', // this stops cell selection from picking the table.
30667             'data-block' : 'Td',
30668             valign : this.valign,
30669             style : {  
30670                 'text-align' :  this.textAlign,
30671                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30672                 'border-collapse' : 'collapse',
30673                 padding : '6px', // 8 for desktop / 4 for mobile
30674                 'vertical-align': this.valign
30675             },
30676             html : this.html
30677         };
30678         if (this.width != '') {
30679             ret.width = this.width;
30680             ret.style.width = this.width;
30681         }
30682         
30683         
30684         if (this.colspan > 1) {
30685             ret.colspan = this.colspan ;
30686         } 
30687         if (this.rowspan > 1) {
30688             ret.rowspan = this.rowspan ;
30689         }
30690         
30691            
30692         
30693         return ret;
30694          
30695     },
30696     
30697     readElement : function(node)
30698     {
30699         node  = node ? node : this.node ;
30700         this.width = node.style.width;
30701         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30702         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30703         this.html = node.innerHTML;
30704         if (node.style.textAlign != '') {
30705             this.textAlign = node.style.textAlign;
30706         }
30707         
30708         
30709     },
30710      
30711     // the default cell object... at present...
30712     emptyCell : function() {
30713         return {
30714             colspan :  1,
30715             rowspan :  1,
30716             textAlign : 'left',
30717             html : "&nbsp;" // is this going to be editable now?
30718         };
30719      
30720     },
30721     
30722     removeNode : function()
30723     {
30724         return this.node.closest('table');
30725          
30726     },
30727     
30728     cellData : false,
30729     
30730     colWidths : false,
30731     
30732     toTableArray  : function()
30733     {
30734         var ret = [];
30735         var tab = this.node.closest('tr').closest('table');
30736         Array.from(tab.rows).forEach(function(r, ri){
30737             ret[ri] = [];
30738         });
30739         var rn = 0;
30740         this.colWidths = [];
30741         var all_auto = true;
30742         Array.from(tab.rows).forEach(function(r, ri){
30743             
30744             var cn = 0;
30745             Array.from(r.cells).forEach(function(ce, ci){
30746                 var c =  {
30747                     cell : ce,
30748                     row : rn,
30749                     col: cn,
30750                     colspan : ce.colSpan,
30751                     rowspan : ce.rowSpan
30752                 };
30753                 if (ce.isEqualNode(this.node)) {
30754                     this.cellData = c;
30755                 }
30756                 // if we have been filled up by a row?
30757                 if (typeof(ret[rn][cn]) != 'undefined') {
30758                     while(typeof(ret[rn][cn]) != 'undefined') {
30759                         cn++;
30760                     }
30761                     c.col = cn;
30762                 }
30763                 
30764                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30765                     this.colWidths[cn] =   ce.style.width;
30766                     if (this.colWidths[cn] != '') {
30767                         all_auto = false;
30768                     }
30769                 }
30770                 
30771                 
30772                 if (c.colspan < 2 && c.rowspan < 2 ) {
30773                     ret[rn][cn] = c;
30774                     cn++;
30775                     return;
30776                 }
30777                 for(var j = 0; j < c.rowspan; j++) {
30778                     if (typeof(ret[rn+j]) == 'undefined') {
30779                         continue; // we have a problem..
30780                     }
30781                     ret[rn+j][cn] = c;
30782                     for(var i = 0; i < c.colspan; i++) {
30783                         ret[rn+j][cn+i] = c;
30784                     }
30785                 }
30786                 
30787                 cn += c.colspan;
30788             }, this);
30789             rn++;
30790         }, this);
30791         
30792         // initalize widths.?
30793         // either all widths or no widths..
30794         if (all_auto) {
30795             this.colWidths[0] = false; // no widths flag.
30796         }
30797         
30798         
30799         return ret;
30800         
30801     },
30802     
30803     
30804     
30805     
30806     mergeRight: function()
30807     {
30808          
30809         // get the contents of the next cell along..
30810         var tr = this.node.closest('tr');
30811         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30812         if (i >= tr.childNodes.length - 1) {
30813             return; // no cells on right to merge with.
30814         }
30815         var table = this.toTableArray();
30816         
30817         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30818             return; // nothing right?
30819         }
30820         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30821         // right cell - must be same rowspan and on the same row.
30822         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30823             return; // right hand side is not same rowspan.
30824         }
30825         
30826         
30827         
30828         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30829         tr.removeChild(rc.cell);
30830         this.colspan += rc.colspan;
30831         this.node.setAttribute('colspan', this.colspan);
30832
30833         var table = this.toTableArray();
30834         this.normalizeWidths(table);
30835         this.updateWidths(table);
30836     },
30837     
30838     
30839     mergeBelow : function()
30840     {
30841         var table = this.toTableArray();
30842         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30843             return; // no row below
30844         }
30845         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30846             return; // nothing right?
30847         }
30848         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30849         
30850         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30851             return; // right hand side is not same rowspan.
30852         }
30853         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30854         rc.cell.parentNode.removeChild(rc.cell);
30855         this.rowspan += rc.rowspan;
30856         this.node.setAttribute('rowspan', this.rowspan);
30857     },
30858     
30859     split: function()
30860     {
30861         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30862             return;
30863         }
30864         var table = this.toTableArray();
30865         var cd = this.cellData;
30866         this.rowspan = 1;
30867         this.colspan = 1;
30868         
30869         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30870              
30871             
30872             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30873                 if (r == cd.row && c == cd.col) {
30874                     this.node.removeAttribute('rowspan');
30875                     this.node.removeAttribute('colspan');
30876                 }
30877                  
30878                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30879                 ntd.removeAttribute('id'); 
30880                 ntd.style.width  = this.colWidths[c];
30881                 ntd.innerHTML = '';
30882                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30883             }
30884             
30885         }
30886         this.redrawAllCells(table);
30887         
30888     },
30889     
30890     
30891     
30892     redrawAllCells: function(table)
30893     {
30894         
30895          
30896         var tab = this.node.closest('tr').closest('table');
30897         var ctr = tab.rows[0].parentNode;
30898         Array.from(tab.rows).forEach(function(r, ri){
30899             
30900             Array.from(r.cells).forEach(function(ce, ci){
30901                 ce.parentNode.removeChild(ce);
30902             });
30903             r.parentNode.removeChild(r);
30904         });
30905         for(var r = 0 ; r < table.length; r++) {
30906             var re = tab.rows[r];
30907             
30908             var re = tab.ownerDocument.createElement('tr');
30909             ctr.appendChild(re);
30910             for(var c = 0 ; c < table[r].length; c++) {
30911                 if (table[r][c].cell === false) {
30912                     continue;
30913                 }
30914                 
30915                 re.appendChild(table[r][c].cell);
30916                  
30917                 table[r][c].cell = false;
30918             }
30919         }
30920         
30921     },
30922     updateWidths : function(table)
30923     {
30924         for(var r = 0 ; r < table.length; r++) {
30925            
30926             for(var c = 0 ; c < table[r].length; c++) {
30927                 if (table[r][c].cell === false) {
30928                     continue;
30929                 }
30930                 
30931                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30932                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30933                     el.width = Math.floor(this.colWidths[c])  +'%';
30934                     el.updateElement(el.node);
30935                 }
30936                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30937                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30938                     var width = 0;
30939                     for(var i = 0; i < table[r][c].colspan; i ++) {
30940                         width += Math.floor(this.colWidths[c + i]);
30941                     }
30942                     el.width = width  +'%';
30943                     el.updateElement(el.node);
30944                 }
30945                 table[r][c].cell = false; // done
30946             }
30947         }
30948     },
30949     normalizeWidths : function(table)
30950     {
30951         if (this.colWidths[0] === false) {
30952             var nw = 100.0 / this.colWidths.length;
30953             this.colWidths.forEach(function(w,i) {
30954                 this.colWidths[i] = nw;
30955             },this);
30956             return;
30957         }
30958     
30959         var t = 0, missing = [];
30960         
30961         this.colWidths.forEach(function(w,i) {
30962             //if you mix % and
30963             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30964             var add =  this.colWidths[i];
30965             if (add > 0) {
30966                 t+=add;
30967                 return;
30968             }
30969             missing.push(i);
30970             
30971             
30972         },this);
30973         var nc = this.colWidths.length;
30974         if (missing.length) {
30975             var mult = (nc - missing.length) / (1.0 * nc);
30976             var t = mult * t;
30977             var ew = (100 -t) / (1.0 * missing.length);
30978             this.colWidths.forEach(function(w,i) {
30979                 if (w > 0) {
30980                     this.colWidths[i] = w * mult;
30981                     return;
30982                 }
30983                 
30984                 this.colWidths[i] = ew;
30985             }, this);
30986             // have to make up numbers..
30987              
30988         }
30989         // now we should have all the widths..
30990         
30991     
30992     },
30993     
30994     shrinkColumn : function()
30995     {
30996         var table = this.toTableArray();
30997         this.normalizeWidths(table);
30998         var col = this.cellData.col;
30999         var nw = this.colWidths[col] * 0.8;
31000         if (nw < 5) {
31001             return;
31002         }
31003         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
31004         this.colWidths.forEach(function(w,i) {
31005             if (i == col) {
31006                  this.colWidths[i] = nw;
31007                 return;
31008             }
31009             this.colWidths[i] += otherAdd
31010         }, this);
31011         this.updateWidths(table);
31012          
31013     },
31014     growColumn : function()
31015     {
31016         var table = this.toTableArray();
31017         this.normalizeWidths(table);
31018         var col = this.cellData.col;
31019         var nw = this.colWidths[col] * 1.2;
31020         if (nw > 90) {
31021             return;
31022         }
31023         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
31024         this.colWidths.forEach(function(w,i) {
31025             if (i == col) {
31026                 this.colWidths[i] = nw;
31027                 return;
31028             }
31029             this.colWidths[i] -= otherSub
31030         }, this);
31031         this.updateWidths(table);
31032          
31033     },
31034     deleteRow : function()
31035     {
31036         // delete this rows 'tr'
31037         // if any of the cells in this row have a rowspan > 1 && row!= this row..
31038         // then reduce the rowspan.
31039         var table = this.toTableArray();
31040         // this.cellData.row;
31041         for (var i =0;i< table[this.cellData.row].length ; i++) {
31042             var c = table[this.cellData.row][i];
31043             if (c.row != this.cellData.row) {
31044                 
31045                 c.rowspan--;
31046                 c.cell.setAttribute('rowspan', c.rowspan);
31047                 continue;
31048             }
31049             if (c.rowspan > 1) {
31050                 c.rowspan--;
31051                 c.cell.setAttribute('rowspan', c.rowspan);
31052             }
31053         }
31054         table.splice(this.cellData.row,1);
31055         this.redrawAllCells(table);
31056         
31057     },
31058     deleteColumn : function()
31059     {
31060         var table = this.toTableArray();
31061         
31062         for (var i =0;i< table.length ; i++) {
31063             var c = table[i][this.cellData.col];
31064             if (c.col != this.cellData.col) {
31065                 table[i][this.cellData.col].colspan--;
31066             } else if (c.colspan > 1) {
31067                 c.colspan--;
31068                 c.cell.setAttribute('colspan', c.colspan);
31069             }
31070             table[i].splice(this.cellData.col,1);
31071         }
31072         
31073         this.redrawAllCells(table);
31074     }
31075     
31076     
31077     
31078     
31079 })
31080
31081 //<script type="text/javascript">
31082
31083 /*
31084  * Based  Ext JS Library 1.1.1
31085  * Copyright(c) 2006-2007, Ext JS, LLC.
31086  * LGPL
31087  *
31088  */
31089  
31090 /**
31091  * @class Roo.HtmlEditorCore
31092  * @extends Roo.Component
31093  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
31094  *
31095  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
31096  */
31097
31098 Roo.HtmlEditorCore = function(config){
31099     
31100     
31101     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
31102     
31103     
31104     this.addEvents({
31105         /**
31106          * @event initialize
31107          * Fires when the editor is fully initialized (including the iframe)
31108          * @param {Roo.HtmlEditorCore} this
31109          */
31110         initialize: true,
31111         /**
31112          * @event activate
31113          * Fires when the editor is first receives the focus. Any insertion must wait
31114          * until after this event.
31115          * @param {Roo.HtmlEditorCore} this
31116          */
31117         activate: true,
31118          /**
31119          * @event beforesync
31120          * Fires before the textarea is updated with content from the editor iframe. Return false
31121          * to cancel the sync.
31122          * @param {Roo.HtmlEditorCore} this
31123          * @param {String} html
31124          */
31125         beforesync: true,
31126          /**
31127          * @event beforepush
31128          * Fires before the iframe editor is updated with content from the textarea. Return false
31129          * to cancel the push.
31130          * @param {Roo.HtmlEditorCore} this
31131          * @param {String} html
31132          */
31133         beforepush: true,
31134          /**
31135          * @event sync
31136          * Fires when the textarea is updated with content from the editor iframe.
31137          * @param {Roo.HtmlEditorCore} this
31138          * @param {String} html
31139          */
31140         sync: true,
31141          /**
31142          * @event push
31143          * Fires when the iframe editor is updated with content from the textarea.
31144          * @param {Roo.HtmlEditorCore} this
31145          * @param {String} html
31146          */
31147         push: true,
31148         
31149         /**
31150          * @event editorevent
31151          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31152          * @param {Roo.HtmlEditorCore} this
31153          */
31154         editorevent: true 
31155         
31156         
31157     });
31158     
31159     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
31160     
31161     // defaults : white / black...
31162     this.applyBlacklists();
31163     
31164     
31165     
31166 };
31167
31168
31169 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
31170
31171
31172      /**
31173      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
31174      */
31175     
31176     owner : false,
31177     
31178      /**
31179      * @cfg {String} css styling for resizing. (used on bootstrap only)
31180      */
31181     resize : false,
31182      /**
31183      * @cfg {Number} height (in pixels)
31184      */   
31185     height: 300,
31186    /**
31187      * @cfg {Number} width (in pixels)
31188      */   
31189     width: 500,
31190      /**
31191      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
31192      *         if you are doing an email editor, this probably needs disabling, it's designed
31193      */
31194     autoClean: true,
31195     
31196     /**
31197      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
31198      */
31199     enableBlocks : true,
31200     /**
31201      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
31202      * 
31203      */
31204     stylesheets: false,
31205      /**
31206      * @cfg {String} language default en - language of text (usefull for rtl languages)
31207      * 
31208      */
31209     language: 'en',
31210     
31211     /**
31212      * @cfg {boolean} allowComments - default false - allow comments in HTML source
31213      *          - by default they are stripped - if you are editing email you may need this.
31214      */
31215     allowComments: false,
31216     // id of frame..
31217     frameId: false,
31218     
31219     // private properties
31220     validationEvent : false,
31221     deferHeight: true,
31222     initialized : false,
31223     activated : false,
31224     sourceEditMode : false,
31225     onFocus : Roo.emptyFn,
31226     iframePad:3,
31227     hideMode:'offsets',
31228     
31229     clearUp: true,
31230     
31231     // blacklist + whitelisted elements..
31232     black: false,
31233     white: false,
31234      
31235     bodyCls : '',
31236
31237     
31238     undoManager : false,
31239     /**
31240      * Protected method that will not generally be called directly. It
31241      * is called when the editor initializes the iframe with HTML contents. Override this method if you
31242      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
31243      */
31244     getDocMarkup : function(){
31245         // body styles..
31246         var st = '';
31247         
31248         // inherit styels from page...?? 
31249         if (this.stylesheets === false) {
31250             
31251             Roo.get(document.head).select('style').each(function(node) {
31252                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
31253             });
31254             
31255             Roo.get(document.head).select('link').each(function(node) { 
31256                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
31257             });
31258             
31259         } else if (!this.stylesheets.length) {
31260                 // simple..
31261                 st = '<style type="text/css">' +
31262                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
31263                    '</style>';
31264         } else {
31265             for (var i in this.stylesheets) {
31266                 if (typeof(this.stylesheets[i]) != 'string') {
31267                     continue;
31268                 }
31269                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
31270             }
31271             
31272         }
31273         
31274         st +=  '<style type="text/css">' +
31275             'IMG { cursor: pointer } ' +
31276         '</style>';
31277         
31278         st += '<meta name="google" content="notranslate">';
31279         
31280         var cls = 'notranslate roo-htmleditor-body';
31281         
31282         if(this.bodyCls.length){
31283             cls += ' ' + this.bodyCls;
31284         }
31285         
31286         return '<html  class="notranslate" translate="no"><head>' + st  +
31287             //<style type="text/css">' +
31288             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
31289             //'</style>' +
31290             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
31291     },
31292
31293     // private
31294     onRender : function(ct, position)
31295     {
31296         var _t = this;
31297         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
31298         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
31299         
31300         
31301         this.el.dom.style.border = '0 none';
31302         this.el.dom.setAttribute('tabIndex', -1);
31303         this.el.addClass('x-hidden hide');
31304         
31305         
31306         
31307         if(Roo.isIE){ // fix IE 1px bogus margin
31308             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31309         }
31310        
31311         
31312         this.frameId = Roo.id();
31313         
31314         var ifcfg = {
31315             tag: 'iframe',
31316             cls: 'form-control', // bootstrap..
31317             id: this.frameId,
31318             name: this.frameId,
31319             frameBorder : 'no',
31320             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
31321         };
31322         if (this.resize) {
31323             ifcfg.style = { resize : this.resize };
31324         }
31325         
31326         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
31327         
31328         
31329         this.iframe = iframe.dom;
31330
31331         this.assignDocWin();
31332         
31333         this.doc.designMode = 'on';
31334        
31335         this.doc.open();
31336         this.doc.write(this.getDocMarkup());
31337         this.doc.close();
31338
31339         
31340         var task = { // must defer to wait for browser to be ready
31341             run : function(){
31342                 //console.log("run task?" + this.doc.readyState);
31343                 this.assignDocWin();
31344                 if(this.doc.body || this.doc.readyState == 'complete'){
31345                     try {
31346                         this.doc.designMode="on";
31347                         
31348                     } catch (e) {
31349                         return;
31350                     }
31351                     Roo.TaskMgr.stop(task);
31352                     this.initEditor.defer(10, this);
31353                 }
31354             },
31355             interval : 10,
31356             duration: 10000,
31357             scope: this
31358         };
31359         Roo.TaskMgr.start(task);
31360
31361     },
31362
31363     // private
31364     onResize : function(w, h)
31365     {
31366          Roo.log('resize: ' +w + ',' + h );
31367         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31368         if(!this.iframe){
31369             return;
31370         }
31371         if(typeof w == 'number'){
31372             
31373             this.iframe.style.width = w + 'px';
31374         }
31375         if(typeof h == 'number'){
31376             
31377             this.iframe.style.height = h + 'px';
31378             if(this.doc){
31379                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31380             }
31381         }
31382         
31383     },
31384
31385     /**
31386      * Toggles the editor between standard and source edit mode.
31387      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31388      */
31389     toggleSourceEdit : function(sourceEditMode){
31390         
31391         this.sourceEditMode = sourceEditMode === true;
31392         
31393         if(this.sourceEditMode){
31394  
31395             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31396             
31397         }else{
31398             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31399             //this.iframe.className = '';
31400             this.deferFocus();
31401         }
31402         //this.setSize(this.owner.wrap.getSize());
31403         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31404     },
31405
31406     
31407   
31408
31409     /**
31410      * Protected method that will not generally be called directly. If you need/want
31411      * custom HTML cleanup, this is the method you should override.
31412      * @param {String} html The HTML to be cleaned
31413      * return {String} The cleaned HTML
31414      */
31415     cleanHtml : function(html)
31416     {
31417         html = String(html);
31418         if(html.length > 5){
31419             if(Roo.isSafari){ // strip safari nonsense
31420                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31421             }
31422         }
31423         if(html == '&nbsp;'){
31424             html = '';
31425         }
31426         return html;
31427     },
31428
31429     /**
31430      * HTML Editor -> Textarea
31431      * Protected method that will not generally be called directly. Syncs the contents
31432      * of the editor iframe with the textarea.
31433      */
31434     syncValue : function()
31435     {
31436         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31437         if(this.initialized){
31438             
31439             if (this.undoManager) {
31440                 this.undoManager.addEvent();
31441             }
31442
31443             
31444             var bd = (this.doc.body || this.doc.documentElement);
31445            
31446             
31447             var sel = this.win.getSelection();
31448             
31449             var div = document.createElement('div');
31450             div.innerHTML = bd.innerHTML;
31451             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31452             if (gtx.length > 0) {
31453                 var rm = gtx.item(0).parentNode;
31454                 rm.parentNode.removeChild(rm);
31455             }
31456             
31457            
31458             if (this.enableBlocks) {
31459                 new Roo.htmleditor.FilterBlock({ node : div });
31460             }
31461             
31462             var html = div.innerHTML;
31463             
31464             //?? tidy?
31465             if (this.autoClean) {
31466                 
31467                 new Roo.htmleditor.FilterAttributes({
31468                     node : div,
31469                     attrib_white : [
31470                             'href',
31471                             'src',
31472                             'name',
31473                             'align',
31474                             'colspan',
31475                             'rowspan',
31476                             'data-display',
31477                             'data-caption-display',
31478                             'data-width',
31479                             'data-caption',
31480                             'start' ,
31481                             'style',
31482                             // youtube embed.
31483                             'class',
31484                             'allowfullscreen',
31485                             'frameborder',
31486                             'width',
31487                             'height',
31488                             'alt'
31489                             ],
31490                     attrib_clean : ['href', 'src' ] 
31491                 });
31492                 
31493                 var tidy = new Roo.htmleditor.TidySerializer({
31494                     inner:  true
31495                 });
31496                 html  = tidy.serialize(div);
31497                 
31498             }
31499             
31500             
31501             if(Roo.isSafari){
31502                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31503                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31504                 if(m && m[1]){
31505                     html = '<div style="'+m[0]+'">' + html + '</div>';
31506                 }
31507             }
31508             html = this.cleanHtml(html);
31509             // fix up the special chars.. normaly like back quotes in word...
31510             // however we do not want to do this with chinese..
31511             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31512                 
31513                 var cc = match.charCodeAt();
31514
31515                 // Get the character value, handling surrogate pairs
31516                 if (match.length == 2) {
31517                     // It's a surrogate pair, calculate the Unicode code point
31518                     var high = match.charCodeAt(0) - 0xD800;
31519                     var low  = match.charCodeAt(1) - 0xDC00;
31520                     cc = (high * 0x400) + low + 0x10000;
31521                 }  else if (
31522                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31523                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31524                     (cc >= 0xf900 && cc < 0xfb00 )
31525                 ) {
31526                         return match;
31527                 }  
31528          
31529                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31530                 return "&#" + cc + ";";
31531                 
31532                 
31533             });
31534             
31535             
31536              
31537             if(this.owner.fireEvent('beforesync', this, html) !== false){
31538                 this.el.dom.value = html;
31539                 this.owner.fireEvent('sync', this, html);
31540             }
31541         }
31542     },
31543
31544     /**
31545      * TEXTAREA -> EDITABLE
31546      * Protected method that will not generally be called directly. Pushes the value of the textarea
31547      * into the iframe editor.
31548      */
31549     pushValue : function()
31550     {
31551         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31552         if(this.initialized){
31553             var v = this.el.dom.value.trim();
31554             
31555             
31556             if(this.owner.fireEvent('beforepush', this, v) !== false){
31557                 var d = (this.doc.body || this.doc.documentElement);
31558                 d.innerHTML = v;
31559                  
31560                 this.el.dom.value = d.innerHTML;
31561                 this.owner.fireEvent('push', this, v);
31562             }
31563             if (this.autoClean) {
31564                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31565                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31566             }
31567             if (this.enableBlocks) {
31568                 Roo.htmleditor.Block.initAll(this.doc.body);
31569             }
31570             
31571             this.updateLanguage();
31572             
31573             var lc = this.doc.body.lastChild;
31574             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31575                 // add an extra line at the end.
31576                 this.doc.body.appendChild(this.doc.createElement('br'));
31577             }
31578             
31579             
31580         }
31581     },
31582
31583     // private
31584     deferFocus : function(){
31585         this.focus.defer(10, this);
31586     },
31587
31588     // doc'ed in Field
31589     focus : function(){
31590         if(this.win && !this.sourceEditMode){
31591             this.win.focus();
31592         }else{
31593             this.el.focus();
31594         }
31595     },
31596     
31597     assignDocWin: function()
31598     {
31599         var iframe = this.iframe;
31600         
31601          if(Roo.isIE){
31602             this.doc = iframe.contentWindow.document;
31603             this.win = iframe.contentWindow;
31604         } else {
31605 //            if (!Roo.get(this.frameId)) {
31606 //                return;
31607 //            }
31608 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31609 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31610             
31611             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31612                 return;
31613             }
31614             
31615             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31616             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31617         }
31618     },
31619     
31620     // private
31621     initEditor : function(){
31622         //console.log("INIT EDITOR");
31623         this.assignDocWin();
31624         
31625         
31626         
31627         this.doc.designMode="on";
31628         this.doc.open();
31629         this.doc.write(this.getDocMarkup());
31630         this.doc.close();
31631         
31632         var dbody = (this.doc.body || this.doc.documentElement);
31633         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31634         // this copies styles from the containing element into thsi one..
31635         // not sure why we need all of this..
31636         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31637         
31638         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31639         //ss['background-attachment'] = 'fixed'; // w3c
31640         dbody.bgProperties = 'fixed'; // ie
31641         dbody.setAttribute("translate", "no");
31642         
31643         //Roo.DomHelper.applyStyles(dbody, ss);
31644         Roo.EventManager.on(this.doc, {
31645              
31646             'mouseup': this.onEditorEvent,
31647             'dblclick': this.onEditorEvent,
31648             'click': this.onEditorEvent,
31649             'keyup': this.onEditorEvent,
31650             
31651             buffer:100,
31652             scope: this
31653         });
31654         Roo.EventManager.on(this.doc, {
31655             'paste': this.onPasteEvent,
31656             scope : this
31657         });
31658         if(Roo.isGecko){
31659             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31660         }
31661         //??? needed???
31662         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31663             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31664         }
31665         this.initialized = true;
31666
31667         
31668         // initialize special key events - enter
31669         new Roo.htmleditor.KeyEnter({core : this});
31670         
31671          
31672         
31673         this.owner.fireEvent('initialize', this);
31674         this.pushValue();
31675     },
31676     // this is to prevent a href clicks resulting in a redirect?
31677    
31678     onPasteEvent : function(e,v)
31679     {
31680         // I think we better assume paste is going to be a dirty load of rubish from word..
31681         
31682         // even pasting into a 'email version' of this widget will have to clean up that mess.
31683         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31684         
31685         // check what type of paste - if it's an image, then handle it differently.
31686         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31687             // pasting images? 
31688             var urlAPI = (window.createObjectURL && window) || 
31689                 (window.URL && URL.revokeObjectURL && URL) || 
31690                 (window.webkitURL && webkitURL);
31691             
31692             var r = new FileReader();
31693             var t = this;
31694             r.addEventListener('load',function()
31695             {
31696                 
31697                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31698                 // is insert asycn?
31699                 if (t.enableBlocks) {
31700                     
31701                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31702                         if (img.closest('figure')) { // assume!! that it's aready
31703                             return;
31704                         }
31705                         var fig  = new Roo.htmleditor.BlockFigure({
31706                             image_src  : img.src
31707                         });
31708                         fig.updateElement(img); // replace it..
31709                         
31710                     });
31711                 }
31712                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31713                 t.owner.fireEvent('paste', this);
31714             });
31715             r.readAsDataURL(cd.files[0]);
31716             
31717             e.preventDefault();
31718             
31719             return false;
31720         }
31721         if (cd.types.indexOf('text/html') < 0 ) {
31722             return false;
31723         }
31724         var images = [];
31725         var html = cd.getData('text/html'); // clipboard event
31726         if (cd.types.indexOf('text/rtf') > -1) {
31727             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31728             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31729         }
31730         // Roo.log(images);
31731         // Roo.log(imgs);
31732         // fixme..
31733         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31734                        .map(function(g) { return g.toDataURL(); })
31735                        .filter(function(g) { return g != 'about:blank'; });
31736         
31737         //Roo.log(html);
31738         html = this.cleanWordChars(html);
31739         
31740         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31741         
31742         
31743         var sn = this.getParentElement();
31744         // check if d contains a table, and prevent nesting??
31745         //Roo.log(d.getElementsByTagName('table'));
31746         //Roo.log(sn);
31747         //Roo.log(sn.closest('table'));
31748         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31749             e.preventDefault();
31750             this.insertAtCursor("You can not nest tables");
31751             //Roo.log("prevent?"); // fixme - 
31752             return false;
31753         }
31754         
31755         
31756         
31757         if (images.length > 0) {
31758             // replace all v:imagedata - with img.
31759             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31760             Roo.each(ar, function(node) {
31761                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31762                 node.parentNode.removeChild(node);
31763             });
31764             
31765             
31766             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31767                 img.setAttribute('src', images[i]);
31768             });
31769         }
31770         if (this.autoClean) {
31771             new Roo.htmleditor.FilterWord({ node : d });
31772             
31773             new Roo.htmleditor.FilterStyleToTag({ node : d });
31774             new Roo.htmleditor.FilterAttributes({
31775                 node : d,
31776                 attrib_white : [
31777                     'href',
31778                     'src',
31779                     'name',
31780                     'align',
31781                     'colspan',
31782                     'rowspan' 
31783                 /*  THESE ARE NOT ALLWOED FOR PASTE
31784                  *    'data-display',
31785                     'data-caption-display',
31786                     'data-width',
31787                     'data-caption',
31788                     'start' ,
31789                     'style',
31790                     // youtube embed.
31791                     'class',
31792                     'allowfullscreen',
31793                     'frameborder',
31794                     'width',
31795                     'height',
31796                     'alt'
31797                     */
31798                     ],
31799                 attrib_clean : ['href', 'src' ] 
31800             });
31801             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31802             // should be fonts..
31803             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31804             new Roo.htmleditor.FilterParagraph({ node : d });
31805             new Roo.htmleditor.FilterSpan({ node : d });
31806             new Roo.htmleditor.FilterLongBr({ node : d });
31807             new Roo.htmleditor.FilterComment({ node : d });
31808             
31809             
31810         }
31811         if (this.enableBlocks) {
31812                 
31813             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31814                 if (img.closest('figure')) { // assume!! that it's aready
31815                     return;
31816                 }
31817                 var fig  = new Roo.htmleditor.BlockFigure({
31818                     image_src  : img.src
31819                 });
31820                 fig.updateElement(img); // replace it..
31821                 
31822             });
31823         }
31824         
31825         
31826         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31827         if (this.enableBlocks) {
31828             Roo.htmleditor.Block.initAll(this.doc.body);
31829         }
31830          
31831         
31832         e.preventDefault();
31833         this.owner.fireEvent('paste', this);
31834         return false;
31835         // default behaveiour should be our local cleanup paste? (optional?)
31836         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31837         //this.owner.fireEvent('paste', e, v);
31838     },
31839     // private
31840     onDestroy : function(){
31841         
31842         
31843         
31844         if(this.rendered){
31845             
31846             //for (var i =0; i < this.toolbars.length;i++) {
31847             //    // fixme - ask toolbars for heights?
31848             //    this.toolbars[i].onDestroy();
31849            // }
31850             
31851             //this.wrap.dom.innerHTML = '';
31852             //this.wrap.remove();
31853         }
31854     },
31855
31856     // private
31857     onFirstFocus : function(){
31858         
31859         this.assignDocWin();
31860         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31861         
31862         this.activated = true;
31863          
31864     
31865         if(Roo.isGecko){ // prevent silly gecko errors
31866             this.win.focus();
31867             var s = this.win.getSelection();
31868             if(!s.focusNode || s.focusNode.nodeType != 3){
31869                 var r = s.getRangeAt(0);
31870                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31871                 r.collapse(true);
31872                 this.deferFocus();
31873             }
31874             try{
31875                 this.execCmd('useCSS', true);
31876                 this.execCmd('styleWithCSS', false);
31877             }catch(e){}
31878         }
31879         this.owner.fireEvent('activate', this);
31880     },
31881
31882     // private
31883     adjustFont: function(btn){
31884         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31885         //if(Roo.isSafari){ // safari
31886         //    adjust *= 2;
31887        // }
31888         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31889         if(Roo.isSafari){ // safari
31890             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31891             v =  (v < 10) ? 10 : v;
31892             v =  (v > 48) ? 48 : v;
31893             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31894             
31895         }
31896         
31897         
31898         v = Math.max(1, v+adjust);
31899         
31900         this.execCmd('FontSize', v  );
31901     },
31902
31903     onEditorEvent : function(e)
31904     {
31905          
31906         
31907         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31908             return; // we do not handle this.. (undo manager does..)
31909         }
31910         // clicking a 'block'?
31911         
31912         // in theory this detects if the last element is not a br, then we try and do that.
31913         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31914         if (e &&
31915             e.target.nodeName == 'BODY' &&
31916             e.type == "mouseup" &&
31917             this.doc.body.lastChild
31918            ) {
31919             var lc = this.doc.body.lastChild;
31920             // gtx-trans is google translate plugin adding crap.
31921             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31922                 lc = lc.previousSibling;
31923             }
31924             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31925             // if last element is <BR> - then dont do anything.
31926             
31927                 var ns = this.doc.createElement('br');
31928                 this.doc.body.appendChild(ns);
31929                 range = this.doc.createRange();
31930                 range.setStartAfter(ns);
31931                 range.collapse(true);
31932                 var sel = this.win.getSelection();
31933                 sel.removeAllRanges();
31934                 sel.addRange(range);
31935             }
31936         }
31937         
31938         
31939         
31940         this.fireEditorEvent(e);
31941       //  this.updateToolbar();
31942         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31943     },
31944     
31945     fireEditorEvent: function(e)
31946     {
31947         this.owner.fireEvent('editorevent', this, e);
31948     },
31949
31950     insertTag : function(tg)
31951     {
31952         // could be a bit smarter... -> wrap the current selected tRoo..
31953         if (tg.toLowerCase() == 'span' ||
31954             tg.toLowerCase() == 'code' ||
31955             tg.toLowerCase() == 'sup' ||
31956             tg.toLowerCase() == 'sub' 
31957             ) {
31958             
31959             range = this.createRange(this.getSelection());
31960             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31961             wrappingNode.appendChild(range.extractContents());
31962             range.insertNode(wrappingNode);
31963
31964             return;
31965             
31966             
31967             
31968         }
31969         this.execCmd("formatblock",   tg);
31970         this.undoManager.addEvent(); 
31971     },
31972     
31973     insertText : function(txt)
31974     {
31975         
31976         
31977         var range = this.createRange();
31978         range.deleteContents();
31979                //alert(Sender.getAttribute('label'));
31980                
31981         range.insertNode(this.doc.createTextNode(txt));
31982         this.undoManager.addEvent();
31983     } ,
31984     
31985      
31986
31987     /**
31988      * Executes a Midas editor command on the editor document and performs necessary focus and
31989      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31990      * @param {String} cmd The Midas command
31991      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31992      */
31993     relayCmd : function(cmd, value)
31994     {
31995         
31996         switch (cmd) {
31997             case 'justifyleft':
31998             case 'justifyright':
31999             case 'justifycenter':
32000                 // if we are in a cell, then we will adjust the
32001                 var n = this.getParentElement();
32002                 var td = n.closest('td');
32003                 if (td) {
32004                     var bl = Roo.htmleditor.Block.factory(td);
32005                     bl.textAlign = cmd.replace('justify','');
32006                     bl.updateElement();
32007                     this.owner.fireEvent('editorevent', this);
32008                     return;
32009                 }
32010                 this.execCmd('styleWithCSS', true); // 
32011                 break;
32012             case 'bold':
32013             case 'italic':
32014             case 'underline':                
32015                 // if there is no selection, then we insert, and set the curson inside it..
32016                 this.execCmd('styleWithCSS', false); 
32017                 break;
32018                 
32019         
32020             default:
32021                 break;
32022         }
32023         
32024         
32025         this.win.focus();
32026         this.execCmd(cmd, value);
32027         this.owner.fireEvent('editorevent', this);
32028         //this.updateToolbar();
32029         this.owner.deferFocus();
32030     },
32031
32032     /**
32033      * Executes a Midas editor command directly on the editor document.
32034      * For visual commands, you should use {@link #relayCmd} instead.
32035      * <b>This should only be called after the editor is initialized.</b>
32036      * @param {String} cmd The Midas command
32037      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
32038      */
32039     execCmd : function(cmd, value){
32040         this.doc.execCommand(cmd, false, value === undefined ? null : value);
32041         this.syncValue();
32042     },
32043  
32044  
32045    
32046     /**
32047      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
32048      * to insert tRoo.
32049      * @param {String} text | dom node.. 
32050      */
32051     insertAtCursor : function(text)
32052     {
32053         
32054         if(!this.activated){
32055             return;
32056         }
32057          
32058         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
32059             this.win.focus();
32060             
32061             
32062             // from jquery ui (MIT licenced)
32063             var range, node;
32064             var win = this.win;
32065             
32066             if (win.getSelection && win.getSelection().getRangeAt) {
32067                 
32068                 // delete the existing?
32069                 
32070                 this.createRange(this.getSelection()).deleteContents();
32071                 range = win.getSelection().getRangeAt(0);
32072                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
32073                 range.insertNode(node);
32074                 range = range.cloneRange();
32075                 range.collapse(false);
32076                  
32077                 win.getSelection().removeAllRanges();
32078                 win.getSelection().addRange(range);
32079                 
32080                 
32081                 
32082             } else if (win.document.selection && win.document.selection.createRange) {
32083                 // no firefox support
32084                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
32085                 win.document.selection.createRange().pasteHTML(txt);
32086             
32087             } else {
32088                 // no firefox support
32089                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
32090                 this.execCmd('InsertHTML', txt);
32091             } 
32092             this.syncValue();
32093             
32094             this.deferFocus();
32095         }
32096     },
32097  // private
32098     mozKeyPress : function(e){
32099         if(e.ctrlKey){
32100             var c = e.getCharCode(), cmd;
32101           
32102             if(c > 0){
32103                 c = String.fromCharCode(c).toLowerCase();
32104                 switch(c){
32105                     case 'b':
32106                         cmd = 'bold';
32107                         break;
32108                     case 'i':
32109                         cmd = 'italic';
32110                         break;
32111                     
32112                     case 'u':
32113                         cmd = 'underline';
32114                         break;
32115                     
32116                     //case 'v':
32117                       //  this.cleanUpPaste.defer(100, this);
32118                       //  return;
32119                         
32120                 }
32121                 if(cmd){
32122                     
32123                     this.relayCmd(cmd);
32124                     //this.win.focus();
32125                     //this.execCmd(cmd);
32126                     //this.deferFocus();
32127                     e.preventDefault();
32128                 }
32129                 
32130             }
32131         }
32132     },
32133
32134     // private
32135     fixKeys : function(){ // load time branching for fastest keydown performance
32136         
32137         
32138         if(Roo.isIE){
32139             return function(e){
32140                 var k = e.getKey(), r;
32141                 if(k == e.TAB){
32142                     e.stopEvent();
32143                     r = this.doc.selection.createRange();
32144                     if(r){
32145                         r.collapse(true);
32146                         r.pasteHTML('&#160;&#160;&#160;&#160;');
32147                         this.deferFocus();
32148                     }
32149                     return;
32150                 }
32151                 /// this is handled by Roo.htmleditor.KeyEnter
32152                  /*
32153                 if(k == e.ENTER){
32154                     r = this.doc.selection.createRange();
32155                     if(r){
32156                         var target = r.parentElement();
32157                         if(!target || target.tagName.toLowerCase() != 'li'){
32158                             e.stopEvent();
32159                             r.pasteHTML('<br/>');
32160                             r.collapse(false);
32161                             r.select();
32162                         }
32163                     }
32164                 }
32165                 */
32166                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
32167                 //    this.cleanUpPaste.defer(100, this);
32168                 //    return;
32169                 //}
32170                 
32171                 
32172             };
32173         }else if(Roo.isOpera){
32174             return function(e){
32175                 var k = e.getKey();
32176                 if(k == e.TAB){
32177                     e.stopEvent();
32178                     this.win.focus();
32179                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
32180                     this.deferFocus();
32181                 }
32182                
32183                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
32184                 //    this.cleanUpPaste.defer(100, this);
32185                  //   return;
32186                 //}
32187                 
32188             };
32189         }else if(Roo.isSafari){
32190             return function(e){
32191                 var k = e.getKey();
32192                 
32193                 if(k == e.TAB){
32194                     e.stopEvent();
32195                     this.execCmd('InsertText','\t');
32196                     this.deferFocus();
32197                     return;
32198                 }
32199                  this.mozKeyPress(e);
32200                 
32201                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
32202                  //   this.cleanUpPaste.defer(100, this);
32203                  //   return;
32204                // }
32205                 
32206              };
32207         }
32208     }(),
32209     
32210     getAllAncestors: function()
32211     {
32212         var p = this.getSelectedNode();
32213         var a = [];
32214         if (!p) {
32215             a.push(p); // push blank onto stack..
32216             p = this.getParentElement();
32217         }
32218         
32219         
32220         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
32221             a.push(p);
32222             p = p.parentNode;
32223         }
32224         a.push(this.doc.body);
32225         return a;
32226     },
32227     lastSel : false,
32228     lastSelNode : false,
32229     
32230     
32231     getSelection : function() 
32232     {
32233         this.assignDocWin();
32234         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
32235     },
32236     /**
32237      * Select a dom node
32238      * @param {DomElement} node the node to select
32239      */
32240     selectNode : function(node, collapse)
32241     {
32242         var nodeRange = node.ownerDocument.createRange();
32243         try {
32244             nodeRange.selectNode(node);
32245         } catch (e) {
32246             nodeRange.selectNodeContents(node);
32247         }
32248         if (collapse === true) {
32249             nodeRange.collapse(true);
32250         }
32251         //
32252         var s = this.win.getSelection();
32253         s.removeAllRanges();
32254         s.addRange(nodeRange);
32255     },
32256     
32257     getSelectedNode: function() 
32258     {
32259         // this may only work on Gecko!!!
32260         
32261         // should we cache this!!!!
32262         
32263          
32264          
32265         var range = this.createRange(this.getSelection()).cloneRange();
32266         
32267         if (Roo.isIE) {
32268             var parent = range.parentElement();
32269             while (true) {
32270                 var testRange = range.duplicate();
32271                 testRange.moveToElementText(parent);
32272                 if (testRange.inRange(range)) {
32273                     break;
32274                 }
32275                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
32276                     break;
32277                 }
32278                 parent = parent.parentElement;
32279             }
32280             return parent;
32281         }
32282         
32283         // is ancestor a text element.
32284         var ac =  range.commonAncestorContainer;
32285         if (ac.nodeType == 3) {
32286             ac = ac.parentNode;
32287         }
32288         
32289         var ar = ac.childNodes;
32290          
32291         var nodes = [];
32292         var other_nodes = [];
32293         var has_other_nodes = false;
32294         for (var i=0;i<ar.length;i++) {
32295             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
32296                 continue;
32297             }
32298             // fullly contained node.
32299             
32300             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
32301                 nodes.push(ar[i]);
32302                 continue;
32303             }
32304             
32305             // probably selected..
32306             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
32307                 other_nodes.push(ar[i]);
32308                 continue;
32309             }
32310             // outer..
32311             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
32312                 continue;
32313             }
32314             
32315             
32316             has_other_nodes = true;
32317         }
32318         if (!nodes.length && other_nodes.length) {
32319             nodes= other_nodes;
32320         }
32321         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32322             return false;
32323         }
32324         
32325         return nodes[0];
32326     },
32327     
32328     
32329     createRange: function(sel)
32330     {
32331         // this has strange effects when using with 
32332         // top toolbar - not sure if it's a great idea.
32333         //this.editor.contentWindow.focus();
32334         if (typeof sel != "undefined") {
32335             try {
32336                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32337             } catch(e) {
32338                 return this.doc.createRange();
32339             }
32340         } else {
32341             return this.doc.createRange();
32342         }
32343     },
32344     getParentElement: function()
32345     {
32346         
32347         this.assignDocWin();
32348         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32349         
32350         var range = this.createRange(sel);
32351          
32352         try {
32353             var p = range.commonAncestorContainer;
32354             while (p.nodeType == 3) { // text node
32355                 p = p.parentNode;
32356             }
32357             return p;
32358         } catch (e) {
32359             return null;
32360         }
32361     
32362     },
32363     /***
32364      *
32365      * Range intersection.. the hard stuff...
32366      *  '-1' = before
32367      *  '0' = hits..
32368      *  '1' = after.
32369      *         [ -- selected range --- ]
32370      *   [fail]                        [fail]
32371      *
32372      *    basically..
32373      *      if end is before start or  hits it. fail.
32374      *      if start is after end or hits it fail.
32375      *
32376      *   if either hits (but other is outside. - then it's not 
32377      *   
32378      *    
32379      **/
32380     
32381     
32382     // @see http://www.thismuchiknow.co.uk/?p=64.
32383     rangeIntersectsNode : function(range, node)
32384     {
32385         var nodeRange = node.ownerDocument.createRange();
32386         try {
32387             nodeRange.selectNode(node);
32388         } catch (e) {
32389             nodeRange.selectNodeContents(node);
32390         }
32391     
32392         var rangeStartRange = range.cloneRange();
32393         rangeStartRange.collapse(true);
32394     
32395         var rangeEndRange = range.cloneRange();
32396         rangeEndRange.collapse(false);
32397     
32398         var nodeStartRange = nodeRange.cloneRange();
32399         nodeStartRange.collapse(true);
32400     
32401         var nodeEndRange = nodeRange.cloneRange();
32402         nodeEndRange.collapse(false);
32403     
32404         return rangeStartRange.compareBoundaryPoints(
32405                  Range.START_TO_START, nodeEndRange) == -1 &&
32406                rangeEndRange.compareBoundaryPoints(
32407                  Range.START_TO_START, nodeStartRange) == 1;
32408         
32409          
32410     },
32411     rangeCompareNode : function(range, node)
32412     {
32413         var nodeRange = node.ownerDocument.createRange();
32414         try {
32415             nodeRange.selectNode(node);
32416         } catch (e) {
32417             nodeRange.selectNodeContents(node);
32418         }
32419         
32420         
32421         range.collapse(true);
32422     
32423         nodeRange.collapse(true);
32424      
32425         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32426         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32427          
32428         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32429         
32430         var nodeIsBefore   =  ss == 1;
32431         var nodeIsAfter    = ee == -1;
32432         
32433         if (nodeIsBefore && nodeIsAfter) {
32434             return 0; // outer
32435         }
32436         if (!nodeIsBefore && nodeIsAfter) {
32437             return 1; //right trailed.
32438         }
32439         
32440         if (nodeIsBefore && !nodeIsAfter) {
32441             return 2;  // left trailed.
32442         }
32443         // fully contined.
32444         return 3;
32445     },
32446  
32447     cleanWordChars : function(input) {// change the chars to hex code
32448         
32449        var swapCodes  = [ 
32450             [    8211, "&#8211;" ], 
32451             [    8212, "&#8212;" ], 
32452             [    8216,  "'" ],  
32453             [    8217, "'" ],  
32454             [    8220, '"' ],  
32455             [    8221, '"' ],  
32456             [    8226, "*" ],  
32457             [    8230, "..." ]
32458         ]; 
32459         var output = input;
32460         Roo.each(swapCodes, function(sw) { 
32461             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32462             
32463             output = output.replace(swapper, sw[1]);
32464         });
32465         
32466         return output;
32467     },
32468     
32469      
32470     
32471         
32472     
32473     cleanUpChild : function (node)
32474     {
32475         
32476         new Roo.htmleditor.FilterComment({node : node});
32477         new Roo.htmleditor.FilterAttributes({
32478                 node : node,
32479                 attrib_black : this.ablack,
32480                 attrib_clean : this.aclean,
32481                 style_white : this.cwhite,
32482                 style_black : this.cblack
32483         });
32484         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32485         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32486          
32487         
32488     },
32489     
32490     /**
32491      * Clean up MS wordisms...
32492      * @deprecated - use filter directly
32493      */
32494     cleanWord : function(node)
32495     {
32496         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32497         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32498         
32499     },
32500    
32501     
32502     /**
32503
32504      * @deprecated - use filters
32505      */
32506     cleanTableWidths : function(node)
32507     {
32508         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32509         
32510  
32511     },
32512     
32513      
32514         
32515     applyBlacklists : function()
32516     {
32517         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32518         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32519         
32520         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32521         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32522         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32523         
32524         this.white = [];
32525         this.black = [];
32526         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32527             if (b.indexOf(tag) > -1) {
32528                 return;
32529             }
32530             this.white.push(tag);
32531             
32532         }, this);
32533         
32534         Roo.each(w, function(tag) {
32535             if (b.indexOf(tag) > -1) {
32536                 return;
32537             }
32538             if (this.white.indexOf(tag) > -1) {
32539                 return;
32540             }
32541             this.white.push(tag);
32542             
32543         }, this);
32544         
32545         
32546         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32547             if (w.indexOf(tag) > -1) {
32548                 return;
32549             }
32550             this.black.push(tag);
32551             
32552         }, this);
32553         
32554         Roo.each(b, function(tag) {
32555             if (w.indexOf(tag) > -1) {
32556                 return;
32557             }
32558             if (this.black.indexOf(tag) > -1) {
32559                 return;
32560             }
32561             this.black.push(tag);
32562             
32563         }, this);
32564         
32565         
32566         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32567         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32568         
32569         this.cwhite = [];
32570         this.cblack = [];
32571         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32572             if (b.indexOf(tag) > -1) {
32573                 return;
32574             }
32575             this.cwhite.push(tag);
32576             
32577         }, this);
32578         
32579         Roo.each(w, function(tag) {
32580             if (b.indexOf(tag) > -1) {
32581                 return;
32582             }
32583             if (this.cwhite.indexOf(tag) > -1) {
32584                 return;
32585             }
32586             this.cwhite.push(tag);
32587             
32588         }, this);
32589         
32590         
32591         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32592             if (w.indexOf(tag) > -1) {
32593                 return;
32594             }
32595             this.cblack.push(tag);
32596             
32597         }, this);
32598         
32599         Roo.each(b, function(tag) {
32600             if (w.indexOf(tag) > -1) {
32601                 return;
32602             }
32603             if (this.cblack.indexOf(tag) > -1) {
32604                 return;
32605             }
32606             this.cblack.push(tag);
32607             
32608         }, this);
32609     },
32610     
32611     setStylesheets : function(stylesheets)
32612     {
32613         if(typeof(stylesheets) == 'string'){
32614             Roo.get(this.iframe.contentDocument.head).createChild({
32615                 tag : 'link',
32616                 rel : 'stylesheet',
32617                 type : 'text/css',
32618                 href : stylesheets
32619             });
32620             
32621             return;
32622         }
32623         var _this = this;
32624      
32625         Roo.each(stylesheets, function(s) {
32626             if(!s.length){
32627                 return;
32628             }
32629             
32630             Roo.get(_this.iframe.contentDocument.head).createChild({
32631                 tag : 'link',
32632                 rel : 'stylesheet',
32633                 type : 'text/css',
32634                 href : s
32635             });
32636         });
32637
32638         
32639     },
32640     
32641     
32642     updateLanguage : function()
32643     {
32644         if (!this.iframe || !this.iframe.contentDocument) {
32645             return;
32646         }
32647         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32648     },
32649     
32650     
32651     removeStylesheets : function()
32652     {
32653         var _this = this;
32654         
32655         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32656             s.remove();
32657         });
32658     },
32659     
32660     setStyle : function(style)
32661     {
32662         Roo.get(this.iframe.contentDocument.head).createChild({
32663             tag : 'style',
32664             type : 'text/css',
32665             html : style
32666         });
32667
32668         return;
32669     }
32670     
32671     // hide stuff that is not compatible
32672     /**
32673      * @event blur
32674      * @hide
32675      */
32676     /**
32677      * @event change
32678      * @hide
32679      */
32680     /**
32681      * @event focus
32682      * @hide
32683      */
32684     /**
32685      * @event specialkey
32686      * @hide
32687      */
32688     /**
32689      * @cfg {String} fieldClass @hide
32690      */
32691     /**
32692      * @cfg {String} focusClass @hide
32693      */
32694     /**
32695      * @cfg {String} autoCreate @hide
32696      */
32697     /**
32698      * @cfg {String} inputType @hide
32699      */
32700     /**
32701      * @cfg {String} invalidClass @hide
32702      */
32703     /**
32704      * @cfg {String} invalidText @hide
32705      */
32706     /**
32707      * @cfg {String} msgFx @hide
32708      */
32709     /**
32710      * @cfg {String} validateOnBlur @hide
32711      */
32712 });
32713
32714 Roo.HtmlEditorCore.white = [
32715         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32716         
32717        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32718        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32719        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32720        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32721        'TABLE',   'UL',         'XMP', 
32722        
32723        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32724       'THEAD',   'TR', 
32725      
32726       'DIR', 'MENU', 'OL', 'UL', 'DL',
32727        
32728       'EMBED',  'OBJECT'
32729 ];
32730
32731
32732 Roo.HtmlEditorCore.black = [
32733     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32734         'APPLET', // 
32735         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32736         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32737         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32738         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32739         //'FONT' // CLEAN LATER..
32740         'COLGROUP', 'COL'   // messy tables.
32741         
32742         
32743 ];
32744 Roo.HtmlEditorCore.clean = [ // ?? needed???
32745      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32746 ];
32747 Roo.HtmlEditorCore.tag_remove = [
32748     'FONT', 'TBODY'  
32749 ];
32750 // attributes..
32751
32752 Roo.HtmlEditorCore.ablack = [
32753     'on'
32754 ];
32755     
32756 Roo.HtmlEditorCore.aclean = [ 
32757     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32758 ];
32759
32760 // protocols..
32761 Roo.HtmlEditorCore.pwhite= [
32762         'http',  'https',  'mailto'
32763 ];
32764
32765 // white listed style attributes.
32766 Roo.HtmlEditorCore.cwhite= [
32767       //  'text-align', /// default is to allow most things..
32768       
32769          
32770 //        'font-size'//??
32771 ];
32772
32773 // black listed style attributes.
32774 Roo.HtmlEditorCore.cblack= [
32775       //  'font-size' -- this can be set by the project 
32776 ];
32777
32778
32779
32780
32781     /*
32782  * - LGPL
32783  *
32784  * HtmlEditor
32785  * 
32786  */
32787
32788 /**
32789  * @class Roo.bootstrap.form.HtmlEditor
32790  * @extends Roo.bootstrap.form.TextArea
32791  * Bootstrap HtmlEditor class
32792
32793  * @constructor
32794  * Create a new HtmlEditor
32795  * @param {Object} config The config object
32796  */
32797
32798 Roo.bootstrap.form.HtmlEditor = function(config){
32799
32800     this.addEvents({
32801             /**
32802              * @event initialize
32803              * Fires when the editor is fully initialized (including the iframe)
32804              * @param {Roo.bootstrap.form.HtmlEditor} this
32805              */
32806             initialize: true,
32807             /**
32808              * @event activate
32809              * Fires when the editor is first receives the focus. Any insertion must wait
32810              * until after this event.
32811              * @param {Roo.bootstrap.form.HtmlEditor} this
32812              */
32813             activate: true,
32814              /**
32815              * @event beforesync
32816              * Fires before the textarea is updated with content from the editor iframe. Return false
32817              * to cancel the sync.
32818              * @param {Roo.bootstrap.form.HtmlEditor} this
32819              * @param {String} html
32820              */
32821             beforesync: true,
32822              /**
32823              * @event beforepush
32824              * Fires before the iframe editor is updated with content from the textarea. Return false
32825              * to cancel the push.
32826              * @param {Roo.bootstrap.form.HtmlEditor} this
32827              * @param {String} html
32828              */
32829             beforepush: true,
32830              /**
32831              * @event sync
32832              * Fires when the textarea is updated with content from the editor iframe.
32833              * @param {Roo.bootstrap.form.HtmlEditor} this
32834              * @param {String} html
32835              */
32836             sync: true,
32837              /**
32838              * @event push
32839              * Fires when the iframe editor is updated with content from the textarea.
32840              * @param {Roo.bootstrap.form.HtmlEditor} this
32841              * @param {String} html
32842              */
32843             push: true,
32844              /**
32845              * @event editmodechange
32846              * Fires when the editor switches edit modes
32847              * @param {Roo.bootstrap.form.HtmlEditor} this
32848              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32849              */
32850             editmodechange: true,
32851             /**
32852              * @event editorevent
32853              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32854              * @param {Roo.bootstrap.form.HtmlEditor} this
32855              */
32856             editorevent: true,
32857             /**
32858              * @event firstfocus
32859              * Fires when on first focus - needed by toolbars..
32860              * @param {Roo.bootstrap.form.HtmlEditor} this
32861              */
32862             firstfocus: true,
32863             /**
32864              * @event autosave
32865              * Auto save the htmlEditor value as a file into Events
32866              * @param {Roo.bootstrap.form.HtmlEditor} this
32867              */
32868             autosave: true,
32869             /**
32870              * @event savedpreview
32871              * preview the saved version of htmlEditor
32872              * @param {Roo.bootstrap.form.HtmlEditor} this
32873              */
32874             savedpreview: true,
32875              /**
32876             * @event stylesheetsclick
32877             * Fires when press the Sytlesheets button
32878             * @param {Roo.HtmlEditorCore} this
32879             */
32880             stylesheetsclick: true,
32881             /**
32882             * @event paste
32883             * Fires when press user pastes into the editor
32884             * @param {Roo.HtmlEditorCore} this
32885             */
32886             paste: true,
32887             /**
32888             * @event imageadd
32889             * Fires when on any editor when an image is added (excluding paste)
32890             * @param {Roo.bootstrap.form.HtmlEditor} this
32891             */
32892            imageadd: true ,
32893             /**
32894             * @event imageupdated
32895             * Fires when on any editor when an image is changed (excluding paste)
32896             * @param {Roo.bootstrap.form.HtmlEditor} this
32897             * @param {HTMLElement} img could also be a figure if blocks are enabled
32898             */
32899            imageupdate: true ,
32900            /**
32901             * @event imagedelete
32902             * Fires when on any editor when an image is deleted
32903             * @param {Roo.bootstrap.form.HtmlEditor} this
32904             * @param {HTMLElement} img could also be a figure if blocks are enabled
32905             * @param {HTMLElement} oldSrc source of image being replaced
32906             */
32907            imagedelete: true  
32908     });
32909     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32910     if (!this.toolbars) {
32911         this.toolbars = [];
32912     }
32913     
32914     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32915     
32916 };
32917
32918
32919 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32920     
32921     
32922       /**
32923      * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32924      */
32925     toolbars : true,
32926     
32927      /**
32928     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32929     */
32930     btns : [],
32931    
32932      /**
32933      * @cfg {String} resize  (none|both|horizontal|vertical) - css resize of element
32934      */
32935     resize : false,
32936      /**
32937      * @cfg {Number} height (in pixels)
32938      */   
32939     height: 300,
32940    /**
32941      * @cfg {Number} width (in pixels)
32942      */   
32943     width: false,
32944     
32945     /**
32946      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32947      * 
32948      */
32949     stylesheets: false,
32950     
32951     // id of frame..
32952     frameId: false,
32953     
32954     // private properties
32955     validationEvent : false,
32956     deferHeight: true,
32957     initialized : false,
32958     activated : false,
32959     
32960     onFocus : Roo.emptyFn,
32961     iframePad:3,
32962     hideMode:'offsets',
32963     
32964     tbContainer : false,
32965     
32966     bodyCls : '',
32967
32968     linkDialogCls : '',
32969     
32970     toolbarContainer :function() {
32971         return this.wrap.select('.x-html-editor-tb',true).first();
32972     },
32973
32974     /**
32975      * Protected method that will not generally be called directly. It
32976      * is called when the editor creates its toolbar. Override this method if you need to
32977      * add custom toolbar buttons.
32978      * @param {HtmlEditor} editor
32979      */
32980     createToolbar : function()
32981     {
32982         //Roo.log('renewing');
32983         //Roo.log("create toolbars");
32984         if (this.toolbars === false) {
32985             return;
32986         }
32987         if (this.toolbars === true) {
32988             this.toolbars = [ 'Standard' ];
32989         }
32990         
32991         var ar = Array.from(this.toolbars);
32992         this.toolbars = [];
32993         ar.forEach(function(t,i) {
32994             if (typeof(t) == 'string') {
32995                 t = {
32996                     xtype : t
32997                 };
32998             }
32999             if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
33000                 t.editor = this;
33001                 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
33002                 t = Roo.factory(t);
33003             }
33004             this.toolbars[i] = t;
33005             this.toolbars[i].render(this.toolbarContainer());
33006         }, this);
33007         
33008         
33009     },
33010
33011      
33012     // private
33013     onRender : function(ct, position)
33014     {
33015        // Roo.log("Call onRender: " + this.xtype);
33016         var _t = this;
33017         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
33018       
33019         this.wrap = this.inputEl().wrap({
33020             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
33021         });
33022         
33023         this.editorcore.onRender(ct, position);
33024          
33025          
33026         this.createToolbar(this);
33027        
33028         
33029           
33030         
33031     },
33032
33033     // private
33034     onResize : function(w, h)
33035     {
33036         Roo.log('resize: ' +w + ',' + h );
33037         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
33038         var ew = false;
33039         var eh = false;
33040         
33041         if(this.inputEl() ){
33042             if(typeof w == 'number'){
33043                 var aw = w - this.wrap.getFrameWidth('lr');
33044                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
33045                 ew = aw;
33046             }
33047             if(typeof h == 'number'){
33048                  var tbh = -11;  // fixme it needs to tool bar size!
33049                 for (var i =0; i < this.toolbars.length;i++) {
33050                     // fixme - ask toolbars for heights?
33051                     tbh += this.toolbars[i].el.getHeight();
33052                     //if (this.toolbars[i].footer) {
33053                     //    tbh += this.toolbars[i].footer.el.getHeight();
33054                     //}
33055                 }
33056               
33057                 
33058                 
33059                 
33060                 
33061                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
33062                 ah -= 5; // knock a few pixes off for look..
33063                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
33064                 var eh = ah;
33065             }
33066         }
33067         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
33068         this.editorcore.onResize(ew,eh);
33069         
33070     },
33071
33072     /**
33073      * Toggles the editor between standard and source edit mode.
33074      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
33075      */
33076     toggleSourceEdit : function(sourceEditMode)
33077     {
33078         this.editorcore.toggleSourceEdit(sourceEditMode);
33079         
33080         if(this.editorcore.sourceEditMode){
33081             Roo.log('editor - showing textarea');
33082             
33083 //            Roo.log('in');
33084 //            Roo.log(this.syncValue());
33085             this.syncValue();
33086             this.inputEl().removeClass(['hide', 'x-hidden']);
33087             this.inputEl().dom.removeAttribute('tabIndex');
33088             this.inputEl().focus();
33089         }else{
33090             Roo.log('editor - hiding textarea');
33091 //            Roo.log('out')
33092 //            Roo.log(this.pushValue()); 
33093             this.pushValue();
33094             
33095             this.inputEl().addClass(['hide', 'x-hidden']);
33096             this.inputEl().dom.setAttribute('tabIndex', -1);
33097             //this.deferFocus();
33098         }
33099          
33100         //if(this.resizable){
33101         //    this.setSize(this.wrap.getSize());
33102         //}
33103         
33104         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
33105     },
33106  
33107     // private (for BoxComponent)
33108     adjustSize : Roo.BoxComponent.prototype.adjustSize,
33109
33110     // private (for BoxComponent)
33111     getResizeEl : function(){
33112         return this.wrap;
33113     },
33114
33115     // private (for BoxComponent)
33116     getPositionEl : function(){
33117         return this.wrap;
33118     },
33119
33120     // private
33121     initEvents : function(){
33122         this.originalValue = this.getValue();
33123     },
33124
33125 //    /**
33126 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
33127 //     * @method
33128 //     */
33129 //    markInvalid : Roo.emptyFn,
33130 //    /**
33131 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
33132 //     * @method
33133 //     */
33134 //    clearInvalid : Roo.emptyFn,
33135
33136     setValue : function(v){
33137         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
33138         this.editorcore.pushValue();
33139     },
33140
33141      
33142     // private
33143     deferFocus : function(){
33144         this.focus.defer(10, this);
33145     },
33146
33147     // doc'ed in Field
33148     focus : function(){
33149         this.editorcore.focus();
33150         
33151     },
33152       
33153
33154     // private
33155     onDestroy : function(){
33156         
33157         
33158         
33159         if(this.rendered){
33160             
33161             for (var i =0; i < this.toolbars.length;i++) {
33162                 // fixme - ask toolbars for heights?
33163                 this.toolbars[i].onDestroy();
33164             }
33165             
33166             this.wrap.dom.innerHTML = '';
33167             this.wrap.remove();
33168         }
33169     },
33170
33171     // private
33172     onFirstFocus : function(){
33173         //Roo.log("onFirstFocus");
33174         this.editorcore.onFirstFocus();
33175          for (var i =0; i < this.toolbars.length;i++) {
33176             this.toolbars[i].onFirstFocus();
33177         }
33178         
33179     },
33180     
33181     // private
33182     syncValue : function()
33183     {   
33184         this.editorcore.syncValue();
33185     },
33186     
33187     pushValue : function()
33188     {   
33189         this.editorcore.pushValue();
33190     }
33191      
33192     
33193     // hide stuff that is not compatible
33194     /**
33195      * @event blur
33196      * @hide
33197      */
33198     /**
33199      * @event change
33200      * @hide
33201      */
33202     /**
33203      * @event focus
33204      * @hide
33205      */
33206     /**
33207      * @event specialkey
33208      * @hide
33209      */
33210     /**
33211      * @cfg {String} fieldClass @hide
33212      */
33213     /**
33214      * @cfg {String} focusClass @hide
33215      */
33216     /**
33217      * @cfg {String} autoCreate @hide
33218      */
33219     /**
33220      * @cfg {String} inputType @hide
33221      */
33222      
33223     /**
33224      * @cfg {String} invalidText @hide
33225      */
33226     /**
33227      * @cfg {String} msgFx @hide
33228      */
33229     /**
33230      * @cfg {String} validateOnBlur @hide
33231      */
33232 });
33233  
33234     
33235    
33236    
33237    
33238       
33239 /**
33240  * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
33241  * @parent Roo.bootstrap.form.HtmlEditor
33242  * @extends Roo.bootstrap.nav.Simplebar
33243  * Basic Toolbar
33244  * 
33245  * @example
33246  * Usage:
33247  *
33248  new Roo.bootstrap.form.HtmlEditor({
33249     ....
33250     toolbars : [
33251         new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
33252             disable : { fonts: 1 , format: 1, ..., ... , ...],
33253             btns : [ .... ]
33254         })
33255     }
33256      
33257  * 
33258  * @cfg {Object} disable List of elements to disable..
33259  * @cfg {Array} btns List of additional buttons.
33260  * 
33261  * 
33262  * NEEDS Extra CSS? 
33263  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
33264  */
33265  
33266 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
33267 {
33268     
33269     Roo.apply(this, config);
33270     
33271     // default disabled, based on 'good practice'..
33272     this.disable = this.disable || {};
33273     Roo.applyIf(this.disable, {
33274         fontSize : true,
33275         colors : true,
33276         specialElements : true
33277     });
33278     Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
33279     
33280     this.editor = config.editor;
33281     this.editorcore = config.editor.editorcore;
33282     
33283     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
33284     
33285     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
33286     // dont call parent... till later.
33287 }
33288 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar,  {
33289      
33290     bar : true,
33291     
33292     editor : false,
33293     editorcore : false,
33294     
33295     
33296     formats : [
33297         "p" ,  
33298         "h1","h2","h3","h4","h5","h6", 
33299         "pre", "code", 
33300         "abbr", "acronym", "address", "cite", "samp", "var",
33301         'div','span'
33302     ],
33303     
33304     
33305     deleteBtn: false,
33306     
33307     onRender : function(ct, position)
33308     {
33309        // Roo.log("Call onRender: " + this.xtype);
33310         
33311        Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
33312        Roo.log(this.el);
33313        this.el.dom.style.marginBottom = '0';
33314        var _this = this;
33315        var editorcore = this.editorcore;
33316        var editor= this.editor;
33317        
33318        var children = [];
33319        var btn = function(id, cmd , toggle, handler, html){
33320        
33321             var  event = toggle ? 'toggle' : 'click';
33322        
33323             var a = {
33324                 size : 'sm',
33325                 xtype: 'Button',
33326                 xns: Roo.bootstrap,
33327                 //glyphicon : id,
33328                 btnid : id,
33329                 fa: id,
33330                 cls : 'roo-html-editor-btn-' + id,
33331                 cmd : cmd, // why id || cmd
33332                 enableToggle: toggle !== false,
33333                 html : html || '',
33334                 pressed : toggle ? false : null,
33335                 listeners : {}
33336             };
33337             a.listeners[toggle ? 'toggle' : 'click'] = function() {
33338                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
33339             };
33340             children.push(a);
33341             return a;
33342        }
33343        
33344     //    var cb_box = function...
33345         
33346         var style = {
33347                 xtype: 'Button',
33348                 size : 'sm',
33349                 xns: Roo.bootstrap,
33350                 fa : 'font',
33351                 cls : 'roo-html-editor-font-chooser',
33352                 //html : 'submit'
33353                 menu : {
33354                     xtype: 'Menu',
33355                     xns: Roo.bootstrap,
33356                     items:  []
33357                 }
33358         };
33359         Roo.each(this.formats, function(f) {
33360             style.menu.items.push({
33361                 xtype :'MenuItem',
33362                 xns: Roo.bootstrap,
33363                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33364                 tagname : f,
33365                 listeners : {
33366                     click : function()
33367                     {
33368                         editorcore.insertTag(this.tagname);
33369                         editor.focus();
33370                     }
33371                 }
33372                 
33373             });
33374         });
33375         children.push(style);   
33376         
33377         btn('bold',         'bold',true);
33378         btn('italic',       'italic',true);
33379         btn('underline',     'underline',true);
33380         btn('align-left',   'justifyleft',true);
33381         btn('align-center', 'justifycenter',true);
33382         btn('align-right' , 'justifyright',true);
33383         btn('link', false, true, this.onLinkClick);
33384         
33385         
33386         btn('image', false, true, this.onImageClick);
33387         btn('list','insertunorderedlist',true);
33388         btn('list-ol','insertorderedlist',true);
33389
33390         btn('pencil', false,true, function(btn){
33391                 Roo.log(this);
33392                 this.toggleSourceEdit(btn.pressed);
33393         });
33394         
33395         if (this.editor.btns.length > 0) {
33396             for (var i = 0; i<this.editor.btns.length; i++) {
33397                 children.push(this.editor.btns[i]);
33398             }
33399         }
33400         
33401         
33402          
33403         this.xtype = 'NavSimplebar'; // why?
33404         
33405         for(var i=0;i< children.length;i++) {
33406             
33407             this.buttons.add(this.addxtypeChild(children[i]));
33408             
33409         }
33410         this.buildToolbarDelete();
33411
33412         editor.on('editorevent', this.updateToolbar, this);
33413     },
33414     
33415     buildToolbarDelete : function()
33416     {
33417         
33418        /* this.addxtypeChild({
33419             xtype : 'Element',
33420             xns : Roo.bootstrap,
33421             cls : 'roo-htmleditor-fill'
33422         });
33423         */
33424         this.deleteBtn = this.addxtypeChild({
33425             size : 'sm',
33426             xtype: 'Button',
33427             xns: Roo.bootstrap,
33428             fa: 'trash',
33429             listeners : {
33430                 click : this.onDelete.createDelegate(this)
33431             }
33432         });
33433         this.deleteBtn.hide();     
33434         
33435     },
33436     
33437     onImageClick : function()
33438     {
33439         if (this.input) {
33440             this.input.un('change', this.onFileSelected, this);
33441         }
33442         this.input = Roo.get(document.body).createChild({ 
33443           tag: 'input', 
33444           type : 'file', 
33445           style : 'display:none', 
33446           multiple: 'multiple'
33447        });
33448         this.input.on('change', this.onFileSelected, this);
33449         this.input.dom.click();
33450     },
33451     
33452     onFileSelected : function(e)
33453     {
33454          e.preventDefault();
33455         
33456         if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33457             return;
33458         }
33459     
33460          
33461         this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33462     },
33463     
33464     addFiles : function(far, fire_add) {
33465
33466          
33467         var editor =  this.editorcore;
33468   
33469         if (!far.length) {
33470             if (fire_add) {
33471                 this.editor.syncValue();
33472                 editor.owner.fireEvent('editorevent', editor.owner, false);
33473                 editor.owner.fireEvent('imageadd', editor.owner, false);
33474             }
33475             return;
33476         }
33477         
33478         var f = far.pop();
33479         
33480         if (!f.type.match(/^image/)) {
33481             this.addFiles(far, fire_add);
33482             return;
33483         }
33484          
33485         var sn = this.selectedNode;
33486         
33487         var bl = sn  && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33488         
33489         
33490         var reader = new FileReader();
33491         reader.addEventListener('load', (function() {
33492             if (bl) {
33493                 var oldSrc = bl.image_src;
33494                 bl.image_src = reader.result;
33495                 //bl.caption = f.name;
33496                 bl.updateElement(sn);
33497                 this.editor.syncValue();
33498                 editor.owner.fireEvent('editorevent', editor.owner, false);
33499                 editor.owner.fireEvent('imageupdate', editor.owner, sn, oldSrc);
33500                 // we only do the first file!! and replace.
33501                 return;
33502             }
33503             if (this.editorcore.enableBlocks) {
33504                 var fig = new Roo.htmleditor.BlockFigure({
33505                     image_src :  reader.result,
33506                     caption : '',
33507                     caption_display : 'none'  //default to hide captions..
33508                  });
33509                 editor.insertAtCursor(fig.toHTML());
33510                 this.addFiles(far, true);
33511                 return;
33512             }
33513             // just a standard img..
33514             if (sn && sn.tagName.toUpperCase() == 'IMG') {
33515                 var oldSrc = sn.src;
33516                 sn.src = reader.result;
33517                 this.editor.syncValue();
33518                 editor.owner.fireEvent('editorevent', editor.owner, false);
33519                 editor.owner.fireEvent('imageupdate', editor.owner, sn, oldSrc);
33520                 return;
33521             }
33522             editor.insertAtCursor('<img src="' + reader.result +'">');
33523             this.addFiles(far, true);
33524             
33525         }).createDelegate(this));
33526         reader.readAsDataURL(f);
33527         
33528     
33529      },
33530     
33531     
33532     onBtnClick : function(id)
33533     {
33534        this.editorcore.relayCmd(id);
33535        this.editorcore.focus();
33536     },
33537     
33538     onLinkClick : function(btn) {
33539         var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33540                 this.selectedNode.getAttribute('href') : '';
33541             
33542         Roo.bootstrap.MessageBox.show({
33543             title : "Add / Edit Link URL",
33544             msg : "Enter the URL for the link",
33545             buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33546             minWidth: 250,
33547             scope : this,
33548             prompt:true,
33549             multiline: false,
33550             modal : true,
33551             value : url,
33552             fn:  function(pressed, newurl) {
33553                 if (pressed != 'ok') {
33554                     this.editorcore.focus();
33555                     return;
33556                 }
33557                 if (url != '') {
33558                     this.selectedNode.setAttribute('href', newurl);
33559                     this.editor.syncValue();
33560                     return;
33561                 }
33562                 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33563                     this.editorcore.relayCmd('createlink', newurl);
33564                 }
33565                 this.editorcore.focus();
33566             },
33567             cls : this.editorcore.linkDialogCls
33568         });
33569     },
33570     /**
33571      * Protected method that will not generally be called directly. It triggers
33572      * a toolbar update by reading the markup state of the current selection in the editor.
33573      */
33574     updateToolbar: function(editor ,ev, sel){
33575
33576         if(!this.editorcore.activated){
33577             this.editor.onFirstFocus(); // is this neeed?
33578             return;
33579         }
33580
33581         var btns = this.buttons; 
33582         var doc = this.editorcore.doc;
33583         var hasToggle  = false;
33584         btns.each(function(e) {
33585             if (e.enableToggle && e.cmd) {
33586                 hasToggle = hasToggle  || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33587                 e.setActive(doc.queryCommandState(e.cmd));
33588             }
33589         }, this);
33590         
33591         
33592         if (ev &&
33593             (ev.type == 'mouseup' || ev.type == 'click' ) &&
33594             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33595             // they have click on an image...
33596             // let's see if we can change the selection...
33597             sel = ev.target;
33598             
33599         }
33600         
33601         var ans = this.editorcore.getAllAncestors();
33602         if (!sel) { 
33603             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
33604             sel = sel ? sel : this.editorcore.doc.body;
33605             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33606             
33607         }
33608         
33609         var lastSel = this.selectedNode;
33610         this.selectedNode = sel;
33611          
33612         // ok see if we are editing a block?
33613         
33614         var db = false;
33615         // you are not actually selecting the block.
33616         if (sel && sel.hasAttribute('data-block')) {
33617             db = sel;
33618         } else if (sel && sel.closest('[data-block]')) {
33619             db = sel.closest('[data-block]');
33620         }
33621         
33622         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33623             e.classList.remove('roo-ed-selection');
33624         });
33625         
33626         var block = false;
33627         if (db && this.editorcore.enableBlocks) {
33628             block = Roo.htmleditor.Block.factory(db);
33629             
33630             if (block) {
33631                 db.className =  (db.classList.length > 0  ? db.className + ' ' : '') +
33632                     ' roo-ed-selection';
33633                 sel = this.selectedNode = db;
33634             }
33635         }
33636         
33637         // highlight the 'a'..
33638         var tn = sel && sel.tagName.toUpperCase() || '';
33639         if (!block && sel && tn != 'A') {
33640             var asel = sel.closest('A');
33641             if (asel) {
33642                 sel = asel;
33643             }
33644         }
33645        
33646         btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33647         btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33648         btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33649         
33650         Roo.bootstrap.menu.Manager.hideAll();
33651          
33652         
33653         
33654         
33655         
33656         // handle delete button..
33657         if (hasToggle || (tn.length && tn == 'BODY')) {
33658             this.deleteBtn.hide();
33659             return;
33660             
33661         }
33662         this.deleteBtn.show();
33663         
33664         
33665         
33666         //this.editorsyncValue();
33667     },
33668     onFirstFocus: function() {
33669         this.buttons.each(function(item){
33670            item.enable();
33671         });
33672     },
33673     
33674     onDelete : function()
33675     {
33676         var range = this.editorcore.createRange();
33677         var selection = this.editorcore.getSelection();
33678         var sn = this.selectedNode;
33679         range.setStart(sn,0);
33680         range.setEnd(sn,0); 
33681         
33682         
33683         if (sn.hasAttribute('data-block')) {
33684             var block = Roo.htmleditor.Block.factory(this.selectedNode);
33685             if (block) {
33686                 sn = block.removeNode();
33687                 sn.parentNode.removeChild(sn);
33688                 selection.removeAllRanges();
33689                 selection.addRange(range);
33690                 this.updateToolbar(null, null, null);
33691                 if (sn.tagName.toUpperCase() == 'FIGURE') {
33692                     this.editor.syncValue();
33693                     this.editor.fireEvent('imagedelete', this.editor, sn);
33694                 }
33695                 
33696                 this.selectedNode = false;
33697                 this.editorcore.fireEditorEvent(false);
33698                 return;
33699             }   
33700              
33701         }
33702         if (!sn) {
33703             return; // should not really happen..
33704         }
33705         if (sn && sn.tagName == 'BODY') {
33706             return;
33707         }
33708         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33709         
33710         // remove and keep parents.
33711         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33712         a.replaceTag(sn);
33713         
33714         selection.removeAllRanges();
33715         selection.addRange(range);
33716         if (sn.tagName.toUpperCase() == 'IMG"') {
33717             this.editor.syncValue();
33718             this.editor.fireEvent('imagedelete', this.editor, sn);
33719         }
33720         
33721         this.selectedNode = false;
33722         this.editorcore.fireEditorEvent(false);
33723         
33724         
33725     },
33726     
33727     
33728     toggleSourceEdit : function(sourceEditMode){
33729         
33730           
33731         if(sourceEditMode){
33732             Roo.log("disabling buttons");
33733            this.buttons.each( function(item){
33734                 if(item.cmd != 'pencil'){
33735                     item.disable();
33736                 }
33737             });
33738           
33739         }else{
33740             Roo.log("enabling buttons");
33741             if(this.editorcore.initialized){
33742                 this.buttons.each( function(item){
33743                     item.enable();
33744                 });
33745             }
33746             
33747         }
33748         Roo.log("calling toggole on editor");
33749         // tell the editor that it's been pressed..
33750         this.editor.toggleSourceEdit(sourceEditMode);
33751        
33752     }
33753 });
33754
33755
33756
33757
33758  
33759 /*
33760  * - LGPL
33761  */
33762
33763 /**
33764  * @class Roo.bootstrap.form.Markdown
33765  * @extends Roo.bootstrap.form.TextArea
33766  * Bootstrap Showdown editable area
33767  * @cfg {string} content
33768  * 
33769  * @constructor
33770  * Create a new Showdown
33771  */
33772
33773 Roo.bootstrap.form.Markdown = function(config){
33774     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33775    
33776 };
33777
33778 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33779     
33780     editing :false,
33781     
33782     initEvents : function()
33783     {
33784         
33785         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33786         this.markdownEl = this.el.createChild({
33787             cls : 'roo-markdown-area'
33788         });
33789         this.inputEl().addClass('d-none');
33790         if (this.getValue() == '') {
33791             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33792             
33793         } else {
33794             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33795         }
33796         this.markdownEl.on('click', this.toggleTextEdit, this);
33797         this.on('blur', this.toggleTextEdit, this);
33798         this.on('specialkey', this.resizeTextArea, this);
33799     },
33800     
33801     toggleTextEdit : function()
33802     {
33803         var sh = this.markdownEl.getHeight();
33804         this.inputEl().addClass('d-none');
33805         this.markdownEl.addClass('d-none');
33806         if (!this.editing) {
33807             // show editor?
33808             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33809             this.inputEl().removeClass('d-none');
33810             this.inputEl().focus();
33811             this.editing = true;
33812             return;
33813         }
33814         // show showdown...
33815         this.updateMarkdown();
33816         this.markdownEl.removeClass('d-none');
33817         this.editing = false;
33818         return;
33819     },
33820     updateMarkdown : function()
33821     {
33822         if (this.getValue() == '') {
33823             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33824             return;
33825         }
33826  
33827         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33828     },
33829     
33830     resizeTextArea: function () {
33831         
33832         var sh = 100;
33833         Roo.log([sh, this.getValue().split("\n").length * 30]);
33834         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33835     },
33836     setValue : function(val)
33837     {
33838         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33839         if (!this.editing) {
33840             this.updateMarkdown();
33841         }
33842         
33843     },
33844     focus : function()
33845     {
33846         if (!this.editing) {
33847             this.toggleTextEdit();
33848         }
33849         
33850     }
33851
33852
33853 });/*
33854  * Based on:
33855  * Ext JS Library 1.1.1
33856  * Copyright(c) 2006-2007, Ext JS, LLC.
33857  *
33858  * Originally Released Under LGPL - original licence link has changed is not relivant.
33859  *
33860  * Fork - LGPL
33861  * <script type="text/javascript">
33862  */
33863  
33864 /**
33865  * @class Roo.bootstrap.PagingToolbar
33866  * @extends Roo.bootstrap.nav.Simplebar
33867  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33868  * @constructor
33869  * Create a new PagingToolbar
33870  * @param {Object} config The config object
33871  * @param {Roo.data.Store} store
33872  */
33873 Roo.bootstrap.PagingToolbar = function(config)
33874 {
33875     // old args format still supported... - xtype is prefered..
33876         // created from xtype...
33877     
33878     this.ds = config.dataSource;
33879     
33880     if (config.store && !this.ds) {
33881         this.store= Roo.factory(config.store, Roo.data);
33882         this.ds = this.store;
33883         this.ds.xmodule = this.xmodule || false;
33884     }
33885     
33886     this.toolbarItems = [];
33887     if (config.items) {
33888         this.toolbarItems = config.items;
33889     }
33890     
33891     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33892     
33893     this.cursor = 0;
33894     
33895     if (this.ds) { 
33896         this.bind(this.ds);
33897     }
33898     
33899     if (Roo.bootstrap.version == 4) {
33900         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33901     } else {
33902         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33903     }
33904     
33905 };
33906
33907 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33908     /**
33909      * @cfg {Roo.bootstrap.Button} buttons[]
33910      * Buttons for the toolbar
33911      */
33912      /**
33913      * @cfg {Roo.data.Store} store
33914      * The underlying data store providing the paged data
33915      */
33916     /**
33917      * @cfg {String/HTMLElement/Element} container
33918      * container The id or element that will contain the toolbar
33919      */
33920     /**
33921      * @cfg {Boolean} displayInfo
33922      * True to display the displayMsg (defaults to false)
33923      */
33924     /**
33925      * @cfg {Number} pageSize
33926      * The number of records to display per page (defaults to 20)
33927      */
33928     pageSize: 20,
33929     /**
33930      * @cfg {String} displayMsg
33931      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33932      */
33933     displayMsg : 'Displaying {0} - {1} of {2}',
33934     /**
33935      * @cfg {String} emptyMsg
33936      * The message to display when no records are found (defaults to "No data to display")
33937      */
33938     emptyMsg : 'No data to display',
33939     /**
33940      * Customizable piece of the default paging text (defaults to "Page")
33941      * @type String
33942      */
33943     beforePageText : "Page",
33944     /**
33945      * Customizable piece of the default paging text (defaults to "of %0")
33946      * @type String
33947      */
33948     afterPageText : "of {0}",
33949     /**
33950      * Customizable piece of the default paging text (defaults to "First Page")
33951      * @type String
33952      */
33953     firstText : "First Page",
33954     /**
33955      * Customizable piece of the default paging text (defaults to "Previous Page")
33956      * @type String
33957      */
33958     prevText : "Previous Page",
33959     /**
33960      * Customizable piece of the default paging text (defaults to "Next Page")
33961      * @type String
33962      */
33963     nextText : "Next Page",
33964     /**
33965      * Customizable piece of the default paging text (defaults to "Last Page")
33966      * @type String
33967      */
33968     lastText : "Last Page",
33969     /**
33970      * Customizable piece of the default paging text (defaults to "Refresh")
33971      * @type String
33972      */
33973     refreshText : "Refresh",
33974
33975     buttons : false,
33976     // private
33977     onRender : function(ct, position) 
33978     {
33979         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33980         this.navgroup.parentId = this.id;
33981         this.navgroup.onRender(this.el, null);
33982         // add the buttons to the navgroup
33983         
33984         if(this.displayInfo){
33985             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33986             this.displayEl = this.el.select('.x-paging-info', true).first();
33987 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33988 //            this.displayEl = navel.el.select('span',true).first();
33989         }
33990         
33991         var _this = this;
33992         
33993         if(this.buttons){
33994             Roo.each(_this.buttons, function(e){ // this might need to use render????
33995                Roo.factory(e).render(_this.el);
33996             });
33997         }
33998             
33999         Roo.each(_this.toolbarItems, function(e) {
34000             _this.navgroup.addItem(e);
34001         });
34002         
34003         
34004         this.first = this.navgroup.addItem({
34005             tooltip: this.firstText,
34006             cls: "prev btn-outline-secondary",
34007             html : ' <i class="fa fa-step-backward"></i>',
34008             disabled: true,
34009             preventDefault: true,
34010             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
34011         });
34012         
34013         this.prev =  this.navgroup.addItem({
34014             tooltip: this.prevText,
34015             cls: "prev btn-outline-secondary",
34016             html : ' <i class="fa fa-backward"></i>',
34017             disabled: true,
34018             preventDefault: true,
34019             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
34020         });
34021     //this.addSeparator();
34022         
34023         
34024         var field = this.navgroup.addItem( {
34025             tagtype : 'span',
34026             cls : 'x-paging-position  btn-outline-secondary',
34027              disabled: true,
34028             html : this.beforePageText  +
34029                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
34030                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
34031          } ); //?? escaped?
34032         
34033         this.field = field.el.select('input', true).first();
34034         this.field.on("keydown", this.onPagingKeydown, this);
34035         this.field.on("focus", function(){this.dom.select();});
34036     
34037     
34038         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
34039         //this.field.setHeight(18);
34040         //this.addSeparator();
34041         this.next = this.navgroup.addItem({
34042             tooltip: this.nextText,
34043             cls: "next btn-outline-secondary",
34044             html : ' <i class="fa fa-forward"></i>',
34045             disabled: true,
34046             preventDefault: true,
34047             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
34048         });
34049         this.last = this.navgroup.addItem({
34050             tooltip: this.lastText,
34051             html : ' <i class="fa fa-step-forward"></i>',
34052             cls: "next btn-outline-secondary",
34053             disabled: true,
34054             preventDefault: true,
34055             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
34056         });
34057     //this.addSeparator();
34058         this.loading = this.navgroup.addItem({
34059             tooltip: this.refreshText,
34060             cls: "btn-outline-secondary",
34061             html : ' <i class="fa fa-refresh"></i>',
34062             preventDefault: true,
34063             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
34064         });
34065         
34066     },
34067
34068     // private
34069     updateInfo : function(){
34070         if(this.displayEl){
34071             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
34072             var msg = count == 0 ?
34073                 this.emptyMsg :
34074                 String.format(
34075                     this.displayMsg,
34076                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
34077                 );
34078             this.displayEl.update(msg);
34079         }
34080     },
34081
34082     // private
34083     onLoad : function(ds, r, o)
34084     {
34085         this.cursor = o.params && o.params.start ? o.params.start : 0;
34086         
34087         var d = this.getPageData(),
34088             ap = d.activePage,
34089             ps = d.pages;
34090         
34091         
34092         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
34093         this.field.dom.value = ap;
34094         this.first.setDisabled(ap == 1);
34095         this.prev.setDisabled(ap == 1);
34096         this.next.setDisabled(ap == ps);
34097         this.last.setDisabled(ap == ps);
34098         this.loading.enable();
34099         this.updateInfo();
34100     },
34101
34102     // private
34103     getPageData : function(){
34104         var total = this.ds.getTotalCount();
34105         return {
34106             total : total,
34107             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
34108             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
34109         };
34110     },
34111
34112     // private
34113     onLoadError : function(proxy, o){
34114         this.loading.enable();
34115         if (this.ds.events.loadexception.listeners.length  < 2) {
34116             // nothing has been assigned to loadexception except this...
34117             // so 
34118             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
34119
34120         }
34121     },
34122
34123     // private
34124     onPagingKeydown : function(e){
34125         var k = e.getKey();
34126         var d = this.getPageData();
34127         if(k == e.RETURN){
34128             var v = this.field.dom.value, pageNum;
34129             if(!v || isNaN(pageNum = parseInt(v, 10))){
34130                 this.field.dom.value = d.activePage;
34131                 return;
34132             }
34133             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
34134             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34135             e.stopEvent();
34136         }
34137         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))
34138         {
34139           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
34140           this.field.dom.value = pageNum;
34141           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
34142           e.stopEvent();
34143         }
34144         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
34145         {
34146           var v = this.field.dom.value, pageNum; 
34147           var increment = (e.shiftKey) ? 10 : 1;
34148           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
34149                 increment *= -1;
34150           }
34151           if(!v || isNaN(pageNum = parseInt(v, 10))) {
34152             this.field.dom.value = d.activePage;
34153             return;
34154           }
34155           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
34156           {
34157             this.field.dom.value = parseInt(v, 10) + increment;
34158             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
34159             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34160           }
34161           e.stopEvent();
34162         }
34163     },
34164
34165     // private
34166     beforeLoad : function(){
34167         if(this.loading){
34168             this.loading.disable();
34169         }
34170     },
34171
34172     // private
34173     onClick : function(which){
34174         
34175         var ds = this.ds;
34176         if (!ds) {
34177             return;
34178         }
34179         
34180         switch(which){
34181             case "first":
34182                 ds.load({params:{start: 0, limit: this.pageSize}});
34183             break;
34184             case "prev":
34185                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
34186             break;
34187             case "next":
34188                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
34189             break;
34190             case "last":
34191                 var total = ds.getTotalCount();
34192                 var extra = total % this.pageSize;
34193                 var lastStart = extra ? (total - extra) : total-this.pageSize;
34194                 ds.load({params:{start: lastStart, limit: this.pageSize}});
34195             break;
34196             case "refresh":
34197                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
34198             break;
34199         }
34200     },
34201
34202     /**
34203      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
34204      * @param {Roo.data.Store} store The data store to unbind
34205      */
34206     unbind : function(ds){
34207         ds.un("beforeload", this.beforeLoad, this);
34208         ds.un("load", this.onLoad, this);
34209         ds.un("loadexception", this.onLoadError, this);
34210         ds.un("remove", this.updateInfo, this);
34211         ds.un("add", this.updateInfo, this);
34212         this.ds = undefined;
34213     },
34214
34215     /**
34216      * Binds the paging toolbar to the specified {@link Roo.data.Store}
34217      * @param {Roo.data.Store} store The data store to bind
34218      */
34219     bind : function(ds){
34220         ds.on("beforeload", this.beforeLoad, this);
34221         ds.on("load", this.onLoad, this);
34222         ds.on("loadexception", this.onLoadError, this);
34223         ds.on("remove", this.updateInfo, this);
34224         ds.on("add", this.updateInfo, this);
34225         this.ds = ds;
34226     }
34227 });/*
34228  * - LGPL
34229  *
34230  * element
34231  * 
34232  */
34233
34234 /**
34235  * @class Roo.bootstrap.MessageBar
34236  * @extends Roo.bootstrap.Component
34237  * Bootstrap MessageBar class
34238  * @cfg {String} html contents of the MessageBar
34239  * @cfg {String} weight (info | success | warning | danger) default info
34240  * @cfg {String} beforeClass insert the bar before the given class
34241  * @cfg {Boolean} closable (true | false) default false
34242  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
34243  * 
34244  * @constructor
34245  * Create a new Element
34246  * @param {Object} config The config object
34247  */
34248
34249 Roo.bootstrap.MessageBar = function(config){
34250     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
34251 };
34252
34253 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
34254     
34255     html: '',
34256     weight: 'info',
34257     closable: false,
34258     fixed: false,
34259     beforeClass: 'bootstrap-sticky-wrap',
34260     
34261     getAutoCreate : function(){
34262         
34263         var cfg = {
34264             tag: 'div',
34265             cls: 'alert alert-dismissable alert-' + this.weight,
34266             cn: [
34267                 {
34268                     tag: 'span',
34269                     cls: 'message',
34270                     html: this.html || ''
34271                 }
34272             ]
34273         };
34274         
34275         if(this.fixed){
34276             cfg.cls += ' alert-messages-fixed';
34277         }
34278         
34279         if(this.closable){
34280             cfg.cn.push({
34281                 tag: 'button',
34282                 cls: 'close',
34283                 html: 'x'
34284             });
34285         }
34286         
34287         return cfg;
34288     },
34289     
34290     onRender : function(ct, position)
34291     {
34292         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
34293         
34294         if(!this.el){
34295             var cfg = Roo.apply({},  this.getAutoCreate());
34296             cfg.id = Roo.id();
34297             
34298             if (this.cls) {
34299                 cfg.cls += ' ' + this.cls;
34300             }
34301             if (this.style) {
34302                 cfg.style = this.style;
34303             }
34304             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
34305             
34306             this.el.setVisibilityMode(Roo.Element.DISPLAY);
34307         }
34308         
34309         this.el.select('>button.close').on('click', this.hide, this);
34310         
34311     },
34312     
34313     show : function()
34314     {
34315         if (!this.rendered) {
34316             this.render();
34317         }
34318         
34319         this.el.show();
34320         
34321         this.fireEvent('show', this);
34322         
34323     },
34324     
34325     hide : function()
34326     {
34327         if (!this.rendered) {
34328             this.render();
34329         }
34330         
34331         this.el.hide();
34332         
34333         this.fireEvent('hide', this);
34334     },
34335     
34336     update : function()
34337     {
34338 //        var e = this.el.dom.firstChild;
34339 //        
34340 //        if(this.closable){
34341 //            e = e.nextSibling;
34342 //        }
34343 //        
34344 //        e.data = this.html || '';
34345
34346         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34347     }
34348    
34349 });
34350
34351  
34352
34353      /*
34354  * - LGPL
34355  *
34356  * Graph
34357  * 
34358  */
34359
34360
34361 /**
34362  * @class Roo.bootstrap.Graph
34363  * @extends Roo.bootstrap.Component
34364  * Bootstrap Graph class
34365 > Prameters
34366  -sm {number} sm 4
34367  -md {number} md 5
34368  @cfg {String} graphtype  bar | vbar | pie
34369  @cfg {number} g_x coodinator | centre x (pie)
34370  @cfg {number} g_y coodinator | centre y (pie)
34371  @cfg {number} g_r radius (pie)
34372  @cfg {number} g_height height of the chart (respected by all elements in the set)
34373  @cfg {number} g_width width of the chart (respected by all elements in the set)
34374  @cfg {Object} title The title of the chart
34375     
34376  -{Array}  values
34377  -opts (object) options for the chart 
34378      o {
34379      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34380      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34381      o vgutter (number)
34382      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.
34383      o stacked (boolean) whether or not to tread values as in a stacked bar chart
34384      o to
34385      o stretch (boolean)
34386      o }
34387  -opts (object) options for the pie
34388      o{
34389      o cut
34390      o startAngle (number)
34391      o endAngle (number)
34392      } 
34393  *
34394  * @constructor
34395  * Create a new Input
34396  * @param {Object} config The config object
34397  */
34398
34399 Roo.bootstrap.Graph = function(config){
34400     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34401     
34402     this.addEvents({
34403         // img events
34404         /**
34405          * @event click
34406          * The img click event for the img.
34407          * @param {Roo.EventObject} e
34408          */
34409         "click" : true
34410     });
34411 };
34412
34413 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
34414     
34415     sm: 4,
34416     md: 5,
34417     graphtype: 'bar',
34418     g_height: 250,
34419     g_width: 400,
34420     g_x: 50,
34421     g_y: 50,
34422     g_r: 30,
34423     opts:{
34424         //g_colors: this.colors,
34425         g_type: 'soft',
34426         g_gutter: '20%'
34427
34428     },
34429     title : false,
34430
34431     getAutoCreate : function(){
34432         
34433         var cfg = {
34434             tag: 'div',
34435             html : null
34436         };
34437         
34438         
34439         return  cfg;
34440     },
34441
34442     onRender : function(ct,position){
34443         
34444         
34445         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34446         
34447         if (typeof(Raphael) == 'undefined') {
34448             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34449             return;
34450         }
34451         
34452         this.raphael = Raphael(this.el.dom);
34453         
34454                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34455                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34456                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34457                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34458                 /*
34459                 r.text(160, 10, "Single Series Chart").attr(txtattr);
34460                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34461                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34462                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34463                 
34464                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34465                 r.barchart(330, 10, 300, 220, data1);
34466                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34467                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34468                 */
34469                 
34470                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34471                 // r.barchart(30, 30, 560, 250,  xdata, {
34472                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34473                 //     axis : "0 0 1 1",
34474                 //     axisxlabels :  xdata
34475                 //     //yvalues : cols,
34476                    
34477                 // });
34478 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34479 //        
34480 //        this.load(null,xdata,{
34481 //                axis : "0 0 1 1",
34482 //                axisxlabels :  xdata
34483 //                });
34484
34485     },
34486
34487     load : function(graphtype,xdata,opts)
34488     {
34489         this.raphael.clear();
34490         if(!graphtype) {
34491             graphtype = this.graphtype;
34492         }
34493         if(!opts){
34494             opts = this.opts;
34495         }
34496         var r = this.raphael,
34497             fin = function () {
34498                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34499             },
34500             fout = function () {
34501                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34502             },
34503             pfin = function() {
34504                 this.sector.stop();
34505                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34506
34507                 if (this.label) {
34508                     this.label[0].stop();
34509                     this.label[0].attr({ r: 7.5 });
34510                     this.label[1].attr({ "font-weight": 800 });
34511                 }
34512             },
34513             pfout = function() {
34514                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34515
34516                 if (this.label) {
34517                     this.label[0].animate({ r: 5 }, 500, "bounce");
34518                     this.label[1].attr({ "font-weight": 400 });
34519                 }
34520             };
34521
34522         switch(graphtype){
34523             case 'bar':
34524                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34525                 break;
34526             case 'hbar':
34527                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34528                 break;
34529             case 'pie':
34530 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
34531 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34532 //            
34533                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34534                 
34535                 break;
34536
34537         }
34538         
34539         if(this.title){
34540             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34541         }
34542         
34543     },
34544     
34545     setTitle: function(o)
34546     {
34547         this.title = o;
34548     },
34549     
34550     initEvents: function() {
34551         
34552         if(!this.href){
34553             this.el.on('click', this.onClick, this);
34554         }
34555     },
34556     
34557     onClick : function(e)
34558     {
34559         Roo.log('img onclick');
34560         this.fireEvent('click', this, e);
34561     }
34562    
34563 });
34564
34565  
34566 Roo.bootstrap.dash = {};/*
34567  * - LGPL
34568  *
34569  * numberBox
34570  * 
34571  */
34572 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34573
34574 /**
34575  * @class Roo.bootstrap.dash.NumberBox
34576  * @extends Roo.bootstrap.Component
34577  * Bootstrap NumberBox class
34578  * @cfg {String} headline Box headline
34579  * @cfg {String} content Box content
34580  * @cfg {String} icon Box icon
34581  * @cfg {String} footer Footer text
34582  * @cfg {String} fhref Footer href
34583  * 
34584  * @constructor
34585  * Create a new NumberBox
34586  * @param {Object} config The config object
34587  */
34588
34589
34590 Roo.bootstrap.dash.NumberBox = function(config){
34591     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34592     
34593 };
34594
34595 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
34596     
34597     headline : '',
34598     content : '',
34599     icon : '',
34600     footer : '',
34601     fhref : '',
34602     ficon : '',
34603     
34604     getAutoCreate : function(){
34605         
34606         var cfg = {
34607             tag : 'div',
34608             cls : 'small-box ',
34609             cn : [
34610                 {
34611                     tag : 'div',
34612                     cls : 'inner',
34613                     cn :[
34614                         {
34615                             tag : 'h3',
34616                             cls : 'roo-headline',
34617                             html : this.headline
34618                         },
34619                         {
34620                             tag : 'p',
34621                             cls : 'roo-content',
34622                             html : this.content
34623                         }
34624                     ]
34625                 }
34626             ]
34627         };
34628         
34629         if(this.icon){
34630             cfg.cn.push({
34631                 tag : 'div',
34632                 cls : 'icon',
34633                 cn :[
34634                     {
34635                         tag : 'i',
34636                         cls : 'ion ' + this.icon
34637                     }
34638                 ]
34639             });
34640         }
34641         
34642         if(this.footer){
34643             var footer = {
34644                 tag : 'a',
34645                 cls : 'small-box-footer',
34646                 href : this.fhref || '#',
34647                 html : this.footer
34648             };
34649             
34650             cfg.cn.push(footer);
34651             
34652         }
34653         
34654         return  cfg;
34655     },
34656
34657     onRender : function(ct,position){
34658         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34659
34660
34661        
34662                 
34663     },
34664
34665     setHeadline: function (value)
34666     {
34667         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34668     },
34669     
34670     setFooter: function (value, href)
34671     {
34672         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34673         
34674         if(href){
34675             this.el.select('a.small-box-footer',true).first().attr('href', href);
34676         }
34677         
34678     },
34679
34680     setContent: function (value)
34681     {
34682         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34683     },
34684
34685     initEvents: function() 
34686     {   
34687         
34688     }
34689     
34690 });
34691
34692  
34693 /*
34694  * - LGPL
34695  *
34696  * TabBox
34697  * 
34698  */
34699 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34700
34701 /**
34702  * @class Roo.bootstrap.dash.TabBox
34703  * @extends Roo.bootstrap.Component
34704  * @children Roo.bootstrap.dash.TabPane
34705  * Bootstrap TabBox class
34706  * @cfg {String} title Title of the TabBox
34707  * @cfg {String} icon Icon of the TabBox
34708  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34709  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34710  * 
34711  * @constructor
34712  * Create a new TabBox
34713  * @param {Object} config The config object
34714  */
34715
34716
34717 Roo.bootstrap.dash.TabBox = function(config){
34718     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34719     this.addEvents({
34720         // raw events
34721         /**
34722          * @event addpane
34723          * When a pane is added
34724          * @param {Roo.bootstrap.dash.TabPane} pane
34725          */
34726         "addpane" : true,
34727         /**
34728          * @event activatepane
34729          * When a pane is activated
34730          * @param {Roo.bootstrap.dash.TabPane} pane
34731          */
34732         "activatepane" : true
34733         
34734          
34735     });
34736     
34737     this.panes = [];
34738 };
34739
34740 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34741
34742     title : '',
34743     icon : false,
34744     showtabs : true,
34745     tabScrollable : false,
34746     
34747     getChildContainer : function()
34748     {
34749         return this.el.select('.tab-content', true).first();
34750     },
34751     
34752     getAutoCreate : function(){
34753         
34754         var header = {
34755             tag: 'li',
34756             cls: 'pull-left header',
34757             html: this.title,
34758             cn : []
34759         };
34760         
34761         if(this.icon){
34762             header.cn.push({
34763                 tag: 'i',
34764                 cls: 'fa ' + this.icon
34765             });
34766         }
34767         
34768         var h = {
34769             tag: 'ul',
34770             cls: 'nav nav-tabs pull-right',
34771             cn: [
34772                 header
34773             ]
34774         };
34775         
34776         if(this.tabScrollable){
34777             h = {
34778                 tag: 'div',
34779                 cls: 'tab-header',
34780                 cn: [
34781                     {
34782                         tag: 'ul',
34783                         cls: 'nav nav-tabs pull-right',
34784                         cn: [
34785                             header
34786                         ]
34787                     }
34788                 ]
34789             };
34790         }
34791         
34792         var cfg = {
34793             tag: 'div',
34794             cls: 'nav-tabs-custom',
34795             cn: [
34796                 h,
34797                 {
34798                     tag: 'div',
34799                     cls: 'tab-content no-padding',
34800                     cn: []
34801                 }
34802             ]
34803         };
34804
34805         return  cfg;
34806     },
34807     initEvents : function()
34808     {
34809         //Roo.log('add add pane handler');
34810         this.on('addpane', this.onAddPane, this);
34811     },
34812      /**
34813      * Updates the box title
34814      * @param {String} html to set the title to.
34815      */
34816     setTitle : function(value)
34817     {
34818         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34819     },
34820     onAddPane : function(pane)
34821     {
34822         this.panes.push(pane);
34823         //Roo.log('addpane');
34824         //Roo.log(pane);
34825         // tabs are rendere left to right..
34826         if(!this.showtabs){
34827             return;
34828         }
34829         
34830         var ctr = this.el.select('.nav-tabs', true).first();
34831          
34832          
34833         var existing = ctr.select('.nav-tab',true);
34834         var qty = existing.getCount();;
34835         
34836         
34837         var tab = ctr.createChild({
34838             tag : 'li',
34839             cls : 'nav-tab' + (qty ? '' : ' active'),
34840             cn : [
34841                 {
34842                     tag : 'a',
34843                     href:'#',
34844                     html : pane.title
34845                 }
34846             ]
34847         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34848         pane.tab = tab;
34849         
34850         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34851         if (!qty) {
34852             pane.el.addClass('active');
34853         }
34854         
34855                 
34856     },
34857     onTabClick : function(ev,un,ob,pane)
34858     {
34859         //Roo.log('tab - prev default');
34860         ev.preventDefault();
34861         
34862         
34863         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34864         pane.tab.addClass('active');
34865         //Roo.log(pane.title);
34866         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34867         // technically we should have a deactivate event.. but maybe add later.
34868         // and it should not de-activate the selected tab...
34869         this.fireEvent('activatepane', pane);
34870         pane.el.addClass('active');
34871         pane.fireEvent('activate');
34872         
34873         
34874     },
34875     
34876     getActivePane : function()
34877     {
34878         var r = false;
34879         Roo.each(this.panes, function(p) {
34880             if(p.el.hasClass('active')){
34881                 r = p;
34882                 return false;
34883             }
34884             
34885             return;
34886         });
34887         
34888         return r;
34889     }
34890     
34891     
34892 });
34893
34894  
34895 /*
34896  * - LGPL
34897  *
34898  * Tab pane
34899  * 
34900  */
34901 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34902 /**
34903  * @class Roo.bootstrap.TabPane
34904  * @extends Roo.bootstrap.Component
34905  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34906  * Bootstrap TabPane class
34907  * @cfg {Boolean} active (false | true) Default false
34908  * @cfg {String} title title of panel
34909
34910  * 
34911  * @constructor
34912  * Create a new TabPane
34913  * @param {Object} config The config object
34914  */
34915
34916 Roo.bootstrap.dash.TabPane = function(config){
34917     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34918     
34919     this.addEvents({
34920         // raw events
34921         /**
34922          * @event activate
34923          * When a pane is activated
34924          * @param {Roo.bootstrap.dash.TabPane} pane
34925          */
34926         "activate" : true
34927          
34928     });
34929 };
34930
34931 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34932     
34933     active : false,
34934     title : '',
34935     
34936     // the tabBox that this is attached to.
34937     tab : false,
34938      
34939     getAutoCreate : function() 
34940     {
34941         var cfg = {
34942             tag: 'div',
34943             cls: 'tab-pane'
34944         };
34945         
34946         if(this.active){
34947             cfg.cls += ' active';
34948         }
34949         
34950         return cfg;
34951     },
34952     initEvents  : function()
34953     {
34954         //Roo.log('trigger add pane handler');
34955         this.parent().fireEvent('addpane', this)
34956     },
34957     
34958      /**
34959      * Updates the tab title 
34960      * @param {String} html to set the title to.
34961      */
34962     setTitle: function(str)
34963     {
34964         if (!this.tab) {
34965             return;
34966         }
34967         this.title = str;
34968         this.tab.select('a', true).first().dom.innerHTML = str;
34969         
34970     }
34971     
34972     
34973     
34974 });
34975
34976  
34977
34978
34979  /*
34980  * - LGPL
34981  *
34982  * Tooltip
34983  * 
34984  */
34985
34986 /**
34987  * @class Roo.bootstrap.Tooltip
34988  * Bootstrap Tooltip class
34989  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34990  * to determine which dom element triggers the tooltip.
34991  * 
34992  * It needs to add support for additional attributes like tooltip-position
34993  * 
34994  * @constructor
34995  * Create a new Toolti
34996  * @param {Object} config The config object
34997  */
34998
34999 Roo.bootstrap.Tooltip = function(config){
35000     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
35001     
35002     this.alignment = Roo.bootstrap.Tooltip.alignment;
35003     
35004     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
35005         this.alignment = config.alignment;
35006     }
35007     
35008 };
35009
35010 Roo.apply(Roo.bootstrap.Tooltip, {
35011     /**
35012      * @function init initialize tooltip monitoring.
35013      * @static
35014      */
35015     currentEl : false,
35016     currentTip : false,
35017     currentRegion : false,
35018     
35019     //  init : delay?
35020     
35021     init : function()
35022     {
35023         Roo.get(document).on('mouseover', this.enter ,this);
35024         Roo.get(document).on('mouseout', this.leave, this);
35025          
35026         
35027         this.currentTip = new Roo.bootstrap.Tooltip();
35028     },
35029     
35030     enter : function(ev)
35031     {
35032         var dom = ev.getTarget();
35033         
35034         //Roo.log(['enter',dom]);
35035         var el = Roo.fly(dom);
35036         if (this.currentEl) {
35037             //Roo.log(dom);
35038             //Roo.log(this.currentEl);
35039             //Roo.log(this.currentEl.contains(dom));
35040             if (this.currentEl == el) {
35041                 return;
35042             }
35043             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
35044                 return;
35045             }
35046
35047         }
35048         
35049         if (this.currentTip.el) {
35050             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
35051         }    
35052         //Roo.log(ev);
35053         
35054         if(!el || el.dom == document){
35055             return;
35056         }
35057         
35058         var bindEl = el; 
35059         var pel = false;
35060         if (!el.attr('tooltip')) {
35061             pel = el.findParent("[tooltip]");
35062             if (pel) {
35063                 bindEl = Roo.get(pel);
35064             }
35065         }
35066         
35067        
35068         
35069         // you can not look for children, as if el is the body.. then everythign is the child..
35070         if (!pel && !el.attr('tooltip')) { //
35071             if (!el.select("[tooltip]").elements.length) {
35072                 return;
35073             }
35074             // is the mouse over this child...?
35075             bindEl = el.select("[tooltip]").first();
35076             var xy = ev.getXY();
35077             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
35078                 //Roo.log("not in region.");
35079                 return;
35080             }
35081             //Roo.log("child element over..");
35082             
35083         }
35084         this.currentEl = el;
35085         this.currentTip.bind(bindEl);
35086         this.currentRegion = Roo.lib.Region.getRegion(dom);
35087         this.currentTip.enter();
35088         
35089     },
35090     leave : function(ev)
35091     {
35092         var dom = ev.getTarget();
35093         //Roo.log(['leave',dom]);
35094         if (!this.currentEl) {
35095             return;
35096         }
35097         
35098         
35099         if (dom != this.currentEl.dom) {
35100             return;
35101         }
35102         var xy = ev.getXY();
35103         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
35104             return;
35105         }
35106         // only activate leave if mouse cursor is outside... bounding box..
35107         
35108         
35109         
35110         
35111         if (this.currentTip) {
35112             this.currentTip.leave();
35113         }
35114         //Roo.log('clear currentEl');
35115         this.currentEl = false;
35116         
35117         
35118     },
35119     alignment : {
35120         'left' : ['r-l', [-2,0], 'right'],
35121         'right' : ['l-r', [2,0], 'left'],
35122         'bottom' : ['t-b', [0,2], 'top'],
35123         'top' : [ 'b-t', [0,-2], 'bottom']
35124     }
35125     
35126 });
35127
35128
35129 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
35130     
35131     
35132     bindEl : false,
35133     
35134     delay : null, // can be { show : 300 , hide: 500}
35135     
35136     timeout : null,
35137     
35138     hoverState : null, //???
35139     
35140     placement : 'bottom', 
35141     
35142     alignment : false,
35143     
35144     getAutoCreate : function(){
35145     
35146         var cfg = {
35147            cls : 'tooltip',   
35148            role : 'tooltip',
35149            cn : [
35150                 {
35151                     cls : 'tooltip-arrow arrow'
35152                 },
35153                 {
35154                     cls : 'tooltip-inner'
35155                 }
35156            ]
35157         };
35158         
35159         return cfg;
35160     },
35161     bind : function(el)
35162     {
35163         this.bindEl = el;
35164     },
35165     
35166     initEvents : function()
35167     {
35168         this.arrowEl = this.el.select('.arrow', true).first();
35169         this.innerEl = this.el.select('.tooltip-inner', true).first();
35170     },
35171     
35172     enter : function () {
35173        
35174         if (this.timeout != null) {
35175             clearTimeout(this.timeout);
35176         }
35177         
35178         this.hoverState = 'in';
35179          //Roo.log("enter - show");
35180         if (!this.delay || !this.delay.show) {
35181             this.show();
35182             return;
35183         }
35184         var _t = this;
35185         this.timeout = setTimeout(function () {
35186             if (_t.hoverState == 'in') {
35187                 _t.show();
35188             }
35189         }, this.delay.show);
35190     },
35191     leave : function()
35192     {
35193         clearTimeout(this.timeout);
35194     
35195         this.hoverState = 'out';
35196          if (!this.delay || !this.delay.hide) {
35197             this.hide();
35198             return;
35199         }
35200        
35201         var _t = this;
35202         this.timeout = setTimeout(function () {
35203             //Roo.log("leave - timeout");
35204             
35205             if (_t.hoverState == 'out') {
35206                 _t.hide();
35207                 Roo.bootstrap.Tooltip.currentEl = false;
35208             }
35209         }, delay);
35210     },
35211     
35212     show : function (msg)
35213     {
35214         if (!this.el) {
35215             this.render(document.body);
35216         }
35217         // set content.
35218         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
35219         
35220         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
35221         
35222         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
35223         
35224         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
35225                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
35226
35227         if(this.bindEl.attr('tooltip-class')) {
35228             this.el.addClass(this.bindEl.attr('tooltip-class'));
35229         }
35230         
35231         var placement = typeof this.placement == 'function' ?
35232             this.placement.call(this, this.el, on_el) :
35233             this.placement;
35234         
35235         if(this.bindEl.attr('tooltip-placement')) {
35236             placement = this.bindEl.attr('tooltip-placement');
35237         }
35238             
35239         var autoToken = /\s?auto?\s?/i;
35240         var autoPlace = autoToken.test(placement);
35241         if (autoPlace) {
35242             placement = placement.replace(autoToken, '') || 'top';
35243         }
35244         
35245         //this.el.detach()
35246         //this.el.setXY([0,0]);
35247         this.el.show();
35248         //this.el.dom.style.display='block';
35249         
35250         //this.el.appendTo(on_el);
35251         
35252         var p = this.getPosition();
35253         var box = this.el.getBox();
35254         
35255         if (autoPlace) {
35256             // fixme..
35257         }
35258         
35259         var align = this.alignment[placement];
35260         
35261         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
35262         
35263         if(placement == 'top' || placement == 'bottom'){
35264             if(xy[0] < 0){
35265                 placement = 'right';
35266             }
35267             
35268             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
35269                 placement = 'left';
35270             }
35271             
35272             var scroll = Roo.select('body', true).first().getScroll();
35273             
35274             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
35275                 placement = 'top';
35276             }
35277             
35278             align = this.alignment[placement];
35279             
35280             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
35281             
35282         }
35283         
35284         var elems = document.getElementsByTagName('div');
35285         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
35286         for (var i = 0; i < elems.length; i++) {
35287           var zindex = Number.parseInt(
35288                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
35289                 10
35290           );
35291           if (zindex > highest) {
35292             highest = zindex;
35293           }
35294         }
35295         
35296         
35297         
35298         this.el.dom.style.zIndex = highest;
35299         
35300         this.el.alignTo(this.bindEl, align[0],align[1]);
35301         //var arrow = this.el.select('.arrow',true).first();
35302         //arrow.set(align[2], 
35303         
35304         this.el.addClass(placement);
35305         this.el.addClass("bs-tooltip-"+ placement);
35306         
35307         this.el.addClass('in fade show');
35308         
35309         this.hoverState = null;
35310         
35311         if (this.el.hasClass('fade')) {
35312             // fade it?
35313         }
35314         
35315         
35316         
35317         
35318         
35319     },
35320     hide : function()
35321     {
35322          
35323         if (!this.el) {
35324             return;
35325         }
35326         //this.el.setXY([0,0]);
35327         if(this.bindEl.attr('tooltip-class')) {
35328             this.el.removeClass(this.bindEl.attr('tooltip-class'));
35329         }
35330         this.el.removeClass(['show', 'in']);
35331         //this.el.hide();
35332         
35333     }
35334     
35335 });
35336  
35337
35338  /*
35339  * - LGPL
35340  *
35341  * Location Picker
35342  * 
35343  */
35344
35345 /**
35346  * @class Roo.bootstrap.LocationPicker
35347  * @extends Roo.bootstrap.Component
35348  * Bootstrap LocationPicker class
35349  * @cfg {Number} latitude Position when init default 0
35350  * @cfg {Number} longitude Position when init default 0
35351  * @cfg {Number} zoom default 15
35352  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35353  * @cfg {Boolean} mapTypeControl default false
35354  * @cfg {Boolean} disableDoubleClickZoom default false
35355  * @cfg {Boolean} scrollwheel default true
35356  * @cfg {Boolean} streetViewControl default false
35357  * @cfg {Number} radius default 0
35358  * @cfg {String} locationName
35359  * @cfg {Boolean} draggable default true
35360  * @cfg {Boolean} enableAutocomplete default false
35361  * @cfg {Boolean} enableReverseGeocode default true
35362  * @cfg {String} markerTitle
35363  * 
35364  * @constructor
35365  * Create a new LocationPicker
35366  * @param {Object} config The config object
35367  */
35368
35369
35370 Roo.bootstrap.LocationPicker = function(config){
35371     
35372     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35373     
35374     this.addEvents({
35375         /**
35376          * @event initial
35377          * Fires when the picker initialized.
35378          * @param {Roo.bootstrap.LocationPicker} this
35379          * @param {Google Location} location
35380          */
35381         initial : true,
35382         /**
35383          * @event positionchanged
35384          * Fires when the picker position changed.
35385          * @param {Roo.bootstrap.LocationPicker} this
35386          * @param {Google Location} location
35387          */
35388         positionchanged : true,
35389         /**
35390          * @event resize
35391          * Fires when the map resize.
35392          * @param {Roo.bootstrap.LocationPicker} this
35393          */
35394         resize : true,
35395         /**
35396          * @event show
35397          * Fires when the map show.
35398          * @param {Roo.bootstrap.LocationPicker} this
35399          */
35400         show : true,
35401         /**
35402          * @event hide
35403          * Fires when the map hide.
35404          * @param {Roo.bootstrap.LocationPicker} this
35405          */
35406         hide : true,
35407         /**
35408          * @event mapClick
35409          * Fires when click the map.
35410          * @param {Roo.bootstrap.LocationPicker} this
35411          * @param {Map event} e
35412          */
35413         mapClick : true,
35414         /**
35415          * @event mapRightClick
35416          * Fires when right click the map.
35417          * @param {Roo.bootstrap.LocationPicker} this
35418          * @param {Map event} e
35419          */
35420         mapRightClick : true,
35421         /**
35422          * @event markerClick
35423          * Fires when click the marker.
35424          * @param {Roo.bootstrap.LocationPicker} this
35425          * @param {Map event} e
35426          */
35427         markerClick : true,
35428         /**
35429          * @event markerRightClick
35430          * Fires when right click the marker.
35431          * @param {Roo.bootstrap.LocationPicker} this
35432          * @param {Map event} e
35433          */
35434         markerRightClick : true,
35435         /**
35436          * @event OverlayViewDraw
35437          * Fires when OverlayView Draw
35438          * @param {Roo.bootstrap.LocationPicker} this
35439          */
35440         OverlayViewDraw : true,
35441         /**
35442          * @event OverlayViewOnAdd
35443          * Fires when OverlayView Draw
35444          * @param {Roo.bootstrap.LocationPicker} this
35445          */
35446         OverlayViewOnAdd : true,
35447         /**
35448          * @event OverlayViewOnRemove
35449          * Fires when OverlayView Draw
35450          * @param {Roo.bootstrap.LocationPicker} this
35451          */
35452         OverlayViewOnRemove : true,
35453         /**
35454          * @event OverlayViewShow
35455          * Fires when OverlayView Draw
35456          * @param {Roo.bootstrap.LocationPicker} this
35457          * @param {Pixel} cpx
35458          */
35459         OverlayViewShow : true,
35460         /**
35461          * @event OverlayViewHide
35462          * Fires when OverlayView Draw
35463          * @param {Roo.bootstrap.LocationPicker} this
35464          */
35465         OverlayViewHide : true,
35466         /**
35467          * @event loadexception
35468          * Fires when load google lib failed.
35469          * @param {Roo.bootstrap.LocationPicker} this
35470          */
35471         loadexception : true
35472     });
35473         
35474 };
35475
35476 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
35477     
35478     gMapContext: false,
35479     
35480     latitude: 0,
35481     longitude: 0,
35482     zoom: 15,
35483     mapTypeId: false,
35484     mapTypeControl: false,
35485     disableDoubleClickZoom: false,
35486     scrollwheel: true,
35487     streetViewControl: false,
35488     radius: 0,
35489     locationName: '',
35490     draggable: true,
35491     enableAutocomplete: false,
35492     enableReverseGeocode: true,
35493     markerTitle: '',
35494     
35495     getAutoCreate: function()
35496     {
35497
35498         var cfg = {
35499             tag: 'div',
35500             cls: 'roo-location-picker'
35501         };
35502         
35503         return cfg
35504     },
35505     
35506     initEvents: function(ct, position)
35507     {       
35508         if(!this.el.getWidth() || this.isApplied()){
35509             return;
35510         }
35511         
35512         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35513         
35514         this.initial();
35515     },
35516     
35517     initial: function()
35518     {
35519         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35520             this.fireEvent('loadexception', this);
35521             return;
35522         }
35523         
35524         if(!this.mapTypeId){
35525             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35526         }
35527         
35528         this.gMapContext = this.GMapContext();
35529         
35530         this.initOverlayView();
35531         
35532         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35533         
35534         var _this = this;
35535                 
35536         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35537             _this.setPosition(_this.gMapContext.marker.position);
35538         });
35539         
35540         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35541             _this.fireEvent('mapClick', this, event);
35542             
35543         });
35544
35545         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35546             _this.fireEvent('mapRightClick', this, event);
35547             
35548         });
35549         
35550         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35551             _this.fireEvent('markerClick', this, event);
35552             
35553         });
35554
35555         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35556             _this.fireEvent('markerRightClick', this, event);
35557             
35558         });
35559         
35560         this.setPosition(this.gMapContext.location);
35561         
35562         this.fireEvent('initial', this, this.gMapContext.location);
35563     },
35564     
35565     initOverlayView: function()
35566     {
35567         var _this = this;
35568         
35569         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35570             
35571             draw: function()
35572             {
35573                 _this.fireEvent('OverlayViewDraw', _this);
35574             },
35575             
35576             onAdd: function()
35577             {
35578                 _this.fireEvent('OverlayViewOnAdd', _this);
35579             },
35580             
35581             onRemove: function()
35582             {
35583                 _this.fireEvent('OverlayViewOnRemove', _this);
35584             },
35585             
35586             show: function(cpx)
35587             {
35588                 _this.fireEvent('OverlayViewShow', _this, cpx);
35589             },
35590             
35591             hide: function()
35592             {
35593                 _this.fireEvent('OverlayViewHide', _this);
35594             }
35595             
35596         });
35597     },
35598     
35599     fromLatLngToContainerPixel: function(event)
35600     {
35601         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35602     },
35603     
35604     isApplied: function() 
35605     {
35606         return this.getGmapContext() == false ? false : true;
35607     },
35608     
35609     getGmapContext: function() 
35610     {
35611         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35612     },
35613     
35614     GMapContext: function() 
35615     {
35616         var position = new google.maps.LatLng(this.latitude, this.longitude);
35617         
35618         var _map = new google.maps.Map(this.el.dom, {
35619             center: position,
35620             zoom: this.zoom,
35621             mapTypeId: this.mapTypeId,
35622             mapTypeControl: this.mapTypeControl,
35623             disableDoubleClickZoom: this.disableDoubleClickZoom,
35624             scrollwheel: this.scrollwheel,
35625             streetViewControl: this.streetViewControl,
35626             locationName: this.locationName,
35627             draggable: this.draggable,
35628             enableAutocomplete: this.enableAutocomplete,
35629             enableReverseGeocode: this.enableReverseGeocode
35630         });
35631         
35632         var _marker = new google.maps.Marker({
35633             position: position,
35634             map: _map,
35635             title: this.markerTitle,
35636             draggable: this.draggable
35637         });
35638         
35639         return {
35640             map: _map,
35641             marker: _marker,
35642             circle: null,
35643             location: position,
35644             radius: this.radius,
35645             locationName: this.locationName,
35646             addressComponents: {
35647                 formatted_address: null,
35648                 addressLine1: null,
35649                 addressLine2: null,
35650                 streetName: null,
35651                 streetNumber: null,
35652                 city: null,
35653                 district: null,
35654                 state: null,
35655                 stateOrProvince: null
35656             },
35657             settings: this,
35658             domContainer: this.el.dom,
35659             geodecoder: new google.maps.Geocoder()
35660         };
35661     },
35662     
35663     drawCircle: function(center, radius, options) 
35664     {
35665         if (this.gMapContext.circle != null) {
35666             this.gMapContext.circle.setMap(null);
35667         }
35668         if (radius > 0) {
35669             radius *= 1;
35670             options = Roo.apply({}, options, {
35671                 strokeColor: "#0000FF",
35672                 strokeOpacity: .35,
35673                 strokeWeight: 2,
35674                 fillColor: "#0000FF",
35675                 fillOpacity: .2
35676             });
35677             
35678             options.map = this.gMapContext.map;
35679             options.radius = radius;
35680             options.center = center;
35681             this.gMapContext.circle = new google.maps.Circle(options);
35682             return this.gMapContext.circle;
35683         }
35684         
35685         return null;
35686     },
35687     
35688     setPosition: function(location) 
35689     {
35690         this.gMapContext.location = location;
35691         this.gMapContext.marker.setPosition(location);
35692         this.gMapContext.map.panTo(location);
35693         this.drawCircle(location, this.gMapContext.radius, {});
35694         
35695         var _this = this;
35696         
35697         if (this.gMapContext.settings.enableReverseGeocode) {
35698             this.gMapContext.geodecoder.geocode({
35699                 latLng: this.gMapContext.location
35700             }, function(results, status) {
35701                 
35702                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35703                     _this.gMapContext.locationName = results[0].formatted_address;
35704                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35705                     
35706                     _this.fireEvent('positionchanged', this, location);
35707                 }
35708             });
35709             
35710             return;
35711         }
35712         
35713         this.fireEvent('positionchanged', this, location);
35714     },
35715     
35716     resize: function()
35717     {
35718         google.maps.event.trigger(this.gMapContext.map, "resize");
35719         
35720         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35721         
35722         this.fireEvent('resize', this);
35723     },
35724     
35725     setPositionByLatLng: function(latitude, longitude)
35726     {
35727         this.setPosition(new google.maps.LatLng(latitude, longitude));
35728     },
35729     
35730     getCurrentPosition: function() 
35731     {
35732         return {
35733             latitude: this.gMapContext.location.lat(),
35734             longitude: this.gMapContext.location.lng()
35735         };
35736     },
35737     
35738     getAddressName: function() 
35739     {
35740         return this.gMapContext.locationName;
35741     },
35742     
35743     getAddressComponents: function() 
35744     {
35745         return this.gMapContext.addressComponents;
35746     },
35747     
35748     address_component_from_google_geocode: function(address_components) 
35749     {
35750         var result = {};
35751         
35752         for (var i = 0; i < address_components.length; i++) {
35753             var component = address_components[i];
35754             if (component.types.indexOf("postal_code") >= 0) {
35755                 result.postalCode = component.short_name;
35756             } else if (component.types.indexOf("street_number") >= 0) {
35757                 result.streetNumber = component.short_name;
35758             } else if (component.types.indexOf("route") >= 0) {
35759                 result.streetName = component.short_name;
35760             } else if (component.types.indexOf("neighborhood") >= 0) {
35761                 result.city = component.short_name;
35762             } else if (component.types.indexOf("locality") >= 0) {
35763                 result.city = component.short_name;
35764             } else if (component.types.indexOf("sublocality") >= 0) {
35765                 result.district = component.short_name;
35766             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35767                 result.stateOrProvince = component.short_name;
35768             } else if (component.types.indexOf("country") >= 0) {
35769                 result.country = component.short_name;
35770             }
35771         }
35772         
35773         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35774         result.addressLine2 = "";
35775         return result;
35776     },
35777     
35778     setZoomLevel: function(zoom)
35779     {
35780         this.gMapContext.map.setZoom(zoom);
35781     },
35782     
35783     show: function()
35784     {
35785         if(!this.el){
35786             return;
35787         }
35788         
35789         this.el.show();
35790         
35791         this.resize();
35792         
35793         this.fireEvent('show', this);
35794     },
35795     
35796     hide: function()
35797     {
35798         if(!this.el){
35799             return;
35800         }
35801         
35802         this.el.hide();
35803         
35804         this.fireEvent('hide', this);
35805     }
35806     
35807 });
35808
35809 Roo.apply(Roo.bootstrap.LocationPicker, {
35810     
35811     OverlayView : function(map, options)
35812     {
35813         options = options || {};
35814         
35815         this.setMap(map);
35816     }
35817     
35818     
35819 });/**
35820  * @class Roo.bootstrap.Alert
35821  * @extends Roo.bootstrap.Component
35822  * Bootstrap Alert class - shows an alert area box
35823  * eg
35824  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35825   Enter a valid email address
35826 </div>
35827  * @licence LGPL
35828  * @cfg {String} title The title of alert
35829  * @cfg {String} html The content of alert
35830  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35831  * @cfg {String} fa font-awesomeicon
35832  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35833  * @cfg {Boolean} close true to show a x closer
35834  * 
35835  * 
35836  * @constructor
35837  * Create a new alert
35838  * @param {Object} config The config object
35839  */
35840
35841
35842 Roo.bootstrap.Alert = function(config){
35843     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35844     
35845 };
35846
35847 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35848     
35849     title: '',
35850     html: '',
35851     weight: false,
35852     fa: false,
35853     faicon: false, // BC
35854     close : false,
35855     
35856     
35857     getAutoCreate : function()
35858     {
35859         
35860         var cfg = {
35861             tag : 'div',
35862             cls : 'alert',
35863             cn : [
35864                 {
35865                     tag: 'button',
35866                     type :  "button",
35867                     cls: "close",
35868                     html : '×',
35869                     style : this.close ? '' : 'display:none'
35870                 },
35871                 {
35872                     tag : 'i',
35873                     cls : 'roo-alert-icon'
35874                     
35875                 },
35876                 {
35877                     tag : 'b',
35878                     cls : 'roo-alert-title',
35879                     html : this.title
35880                 },
35881                 {
35882                     tag : 'span',
35883                     cls : 'roo-alert-text',
35884                     html : this.html
35885                 }
35886             ]
35887         };
35888         
35889         if(this.faicon){
35890             cfg.cn[0].cls += ' fa ' + this.faicon;
35891         }
35892         if(this.fa){
35893             cfg.cn[0].cls += ' fa ' + this.fa;
35894         }
35895         
35896         if(this.weight){
35897             cfg.cls += ' alert-' + this.weight;
35898         }
35899         
35900         return cfg;
35901     },
35902     
35903     initEvents: function() 
35904     {
35905         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35906         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35907         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35908         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35909         if (this.seconds > 0) {
35910             this.hide.defer(this.seconds, this);
35911         }
35912     },
35913     /**
35914      * Set the Title Message HTML
35915      * @param {String} html
35916      */
35917     setTitle : function(str)
35918     {
35919         this.titleEl.dom.innerHTML = str;
35920     },
35921      
35922      /**
35923      * Set the Body Message HTML
35924      * @param {String} html
35925      */
35926     setHtml : function(str)
35927     {
35928         this.htmlEl.dom.innerHTML = str;
35929     },
35930     /**
35931      * Set the Weight of the alert
35932      * @param {String} (success|info|warning|danger) weight
35933      */
35934     
35935     setWeight : function(weight)
35936     {
35937         if(this.weight){
35938             this.el.removeClass('alert-' + this.weight);
35939         }
35940         
35941         this.weight = weight;
35942         
35943         this.el.addClass('alert-' + this.weight);
35944     },
35945       /**
35946      * Set the Icon of the alert
35947      * @param {String} see fontawsome names (name without the 'fa-' bit)
35948      */
35949     setIcon : function(icon)
35950     {
35951         if(this.faicon){
35952             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35953         }
35954         
35955         this.faicon = icon;
35956         
35957         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35958     },
35959     /**
35960      * Hide the Alert
35961      */
35962     hide: function() 
35963     {
35964         this.el.hide();   
35965     },
35966     /**
35967      * Show the Alert
35968      */
35969     show: function() 
35970     {  
35971         this.el.show();   
35972     }
35973     
35974 });
35975
35976  
35977 /*
35978 * Licence: LGPL
35979 */
35980
35981 /**
35982  * @class Roo.bootstrap.UploadCropbox
35983  * @extends Roo.bootstrap.Component
35984  * Bootstrap UploadCropbox class
35985  * @cfg {String} emptyText show when image has been loaded
35986  * @cfg {String} rotateNotify show when image too small to rotate
35987  * @cfg {Number} errorTimeout default 3000
35988  * @cfg {Number} minWidth default 300
35989  * @cfg {Number} minHeight default 300
35990  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35991  * @cfg {Boolean} isDocument (true|false) default false
35992  * @cfg {String} url action url
35993  * @cfg {String} paramName default 'imageUpload'
35994  * @cfg {String} method default POST
35995  * @cfg {Boolean} loadMask (true|false) default true
35996  * @cfg {Boolean} loadingText default 'Loading...'
35997  * 
35998  * @constructor
35999  * Create a new UploadCropbox
36000  * @param {Object} config The config object
36001  */
36002
36003 Roo.bootstrap.UploadCropbox = function(config){
36004     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
36005     
36006     this.addEvents({
36007         /**
36008          * @event beforeselectfile
36009          * Fire before select file
36010          * @param {Roo.bootstrap.UploadCropbox} this
36011          */
36012         "beforeselectfile" : true,
36013         /**
36014          * @event initial
36015          * Fire after initEvent
36016          * @param {Roo.bootstrap.UploadCropbox} this
36017          */
36018         "initial" : true,
36019         /**
36020          * @event crop
36021          * Fire after initEvent
36022          * @param {Roo.bootstrap.UploadCropbox} this
36023          * @param {String} data
36024          */
36025         "crop" : true,
36026         /**
36027          * @event prepare
36028          * Fire when preparing the file data
36029          * @param {Roo.bootstrap.UploadCropbox} this
36030          * @param {Object} file
36031          */
36032         "prepare" : true,
36033         /**
36034          * @event exception
36035          * Fire when get exception
36036          * @param {Roo.bootstrap.UploadCropbox} this
36037          * @param {XMLHttpRequest} xhr
36038          */
36039         "exception" : true,
36040         /**
36041          * @event beforeloadcanvas
36042          * Fire before load the canvas
36043          * @param {Roo.bootstrap.UploadCropbox} this
36044          * @param {String} src
36045          */
36046         "beforeloadcanvas" : true,
36047         /**
36048          * @event trash
36049          * Fire when trash image
36050          * @param {Roo.bootstrap.UploadCropbox} this
36051          */
36052         "trash" : true,
36053         /**
36054          * @event download
36055          * Fire when download the image
36056          * @param {Roo.bootstrap.UploadCropbox} this
36057          */
36058         "download" : true,
36059         /**
36060          * @event footerbuttonclick
36061          * Fire when footerbuttonclick
36062          * @param {Roo.bootstrap.UploadCropbox} this
36063          * @param {String} type
36064          */
36065         "footerbuttonclick" : true,
36066         /**
36067          * @event resize
36068          * Fire when resize
36069          * @param {Roo.bootstrap.UploadCropbox} this
36070          */
36071         "resize" : true,
36072         /**
36073          * @event rotate
36074          * Fire when rotate the image
36075          * @param {Roo.bootstrap.UploadCropbox} this
36076          * @param {String} pos
36077          */
36078         "rotate" : true,
36079         /**
36080          * @event inspect
36081          * Fire when inspect the file
36082          * @param {Roo.bootstrap.UploadCropbox} this
36083          * @param {Object} file
36084          */
36085         "inspect" : true,
36086         /**
36087          * @event upload
36088          * Fire when xhr upload the file
36089          * @param {Roo.bootstrap.UploadCropbox} this
36090          * @param {Object} data
36091          */
36092         "upload" : true,
36093         /**
36094          * @event arrange
36095          * Fire when arrange the file data
36096          * @param {Roo.bootstrap.UploadCropbox} this
36097          * @param {Object} formData
36098          */
36099         "arrange" : true
36100     });
36101     
36102     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
36103 };
36104
36105 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
36106     
36107     emptyText : 'Click to upload image',
36108     rotateNotify : 'Image is too small to rotate',
36109     errorTimeout : 3000,
36110     scale : 0,
36111     baseScale : 1,
36112     rotate : 0,
36113     dragable : false,
36114     pinching : false,
36115     mouseX : 0,
36116     mouseY : 0,
36117     cropData : false,
36118     minWidth : 300,
36119     minHeight : 300,
36120     file : false,
36121     exif : {},
36122     baseRotate : 1,
36123     cropType : 'image/jpeg',
36124     buttons : false,
36125     canvasLoaded : false,
36126     isDocument : false,
36127     method : 'POST',
36128     paramName : 'imageUpload',
36129     loadMask : true,
36130     loadingText : 'Loading...',
36131     maskEl : false,
36132     
36133     getAutoCreate : function()
36134     {
36135         var cfg = {
36136             tag : 'div',
36137             cls : 'roo-upload-cropbox',
36138             cn : [
36139                 {
36140                     tag : 'input',
36141                     cls : 'roo-upload-cropbox-selector',
36142                     type : 'file'
36143                 },
36144                 {
36145                     tag : 'div',
36146                     cls : 'roo-upload-cropbox-body',
36147                     style : 'cursor:pointer',
36148                     cn : [
36149                         {
36150                             tag : 'div',
36151                             cls : 'roo-upload-cropbox-preview'
36152                         },
36153                         {
36154                             tag : 'div',
36155                             cls : 'roo-upload-cropbox-thumb'
36156                         },
36157                         {
36158                             tag : 'div',
36159                             cls : 'roo-upload-cropbox-empty-notify',
36160                             html : this.emptyText
36161                         },
36162                         {
36163                             tag : 'div',
36164                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
36165                             html : this.rotateNotify
36166                         }
36167                     ]
36168                 },
36169                 {
36170                     tag : 'div',
36171                     cls : 'roo-upload-cropbox-footer',
36172                     cn : {
36173                         tag : 'div',
36174                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
36175                         cn : []
36176                     }
36177                 }
36178             ]
36179         };
36180         
36181         return cfg;
36182     },
36183     
36184     onRender : function(ct, position)
36185     {
36186         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
36187         
36188         if (this.buttons.length) {
36189             
36190             Roo.each(this.buttons, function(bb) {
36191                 
36192                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
36193                 
36194                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
36195                 
36196             }, this);
36197         }
36198         
36199         if(this.loadMask){
36200             this.maskEl = this.el;
36201         }
36202     },
36203     
36204     initEvents : function()
36205     {
36206         this.urlAPI = (window.createObjectURL && window) || 
36207                                 (window.URL && URL.revokeObjectURL && URL) || 
36208                                 (window.webkitURL && webkitURL);
36209                         
36210         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
36211         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36212         
36213         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
36214         this.selectorEl.hide();
36215         
36216         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
36217         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36218         
36219         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
36220         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36221         this.thumbEl.hide();
36222         
36223         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
36224         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36225         
36226         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
36227         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36228         this.errorEl.hide();
36229         
36230         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
36231         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36232         this.footerEl.hide();
36233         
36234         this.setThumbBoxSize();
36235         
36236         this.bind();
36237         
36238         this.resize();
36239         
36240         this.fireEvent('initial', this);
36241     },
36242
36243     bind : function()
36244     {
36245         var _this = this;
36246         
36247         window.addEventListener("resize", function() { _this.resize(); } );
36248         
36249         this.bodyEl.on('click', this.beforeSelectFile, this);
36250         
36251         if(Roo.isTouch){
36252             this.bodyEl.on('touchstart', this.onTouchStart, this);
36253             this.bodyEl.on('touchmove', this.onTouchMove, this);
36254             this.bodyEl.on('touchend', this.onTouchEnd, this);
36255         }
36256         
36257         if(!Roo.isTouch){
36258             this.bodyEl.on('mousedown', this.onMouseDown, this);
36259             this.bodyEl.on('mousemove', this.onMouseMove, this);
36260             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
36261             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
36262             Roo.get(document).on('mouseup', this.onMouseUp, this);
36263         }
36264         
36265         this.selectorEl.on('change', this.onFileSelected, this);
36266     },
36267     
36268     reset : function()
36269     {    
36270         this.scale = 0;
36271         this.baseScale = 1;
36272         this.rotate = 0;
36273         this.baseRotate = 1;
36274         this.dragable = false;
36275         this.pinching = false;
36276         this.mouseX = 0;
36277         this.mouseY = 0;
36278         this.cropData = false;
36279         this.notifyEl.dom.innerHTML = this.emptyText;
36280         
36281         this.selectorEl.dom.value = '';
36282         
36283     },
36284     
36285     resize : function()
36286     {
36287         if(this.fireEvent('resize', this) != false){
36288             this.setThumbBoxPosition();
36289             this.setCanvasPosition();
36290         }
36291     },
36292     
36293     onFooterButtonClick : function(e, el, o, type)
36294     {
36295         switch (type) {
36296             case 'rotate-left' :
36297                 this.onRotateLeft(e);
36298                 break;
36299             case 'rotate-right' :
36300                 this.onRotateRight(e);
36301                 break;
36302             case 'picture' :
36303                 this.beforeSelectFile(e);
36304                 break;
36305             case 'trash' :
36306                 this.trash(e);
36307                 break;
36308             case 'crop' :
36309                 this.crop(e);
36310                 break;
36311             case 'download' :
36312                 this.download(e);
36313                 break;
36314             default :
36315                 break;
36316         }
36317         
36318         this.fireEvent('footerbuttonclick', this, type);
36319     },
36320     
36321     beforeSelectFile : function(e)
36322     {
36323         e.preventDefault();
36324         
36325         if(this.fireEvent('beforeselectfile', this) != false){
36326             this.selectorEl.dom.click();
36327         }
36328     },
36329     
36330     onFileSelected : function(e)
36331     {
36332         e.preventDefault();
36333         
36334         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36335             return;
36336         }
36337         
36338         var file = this.selectorEl.dom.files[0];
36339         
36340         if(this.fireEvent('inspect', this, file) != false){
36341             this.prepare(file);
36342         }
36343         
36344     },
36345     
36346     trash : function(e)
36347     {
36348         this.fireEvent('trash', this);
36349     },
36350     
36351     download : function(e)
36352     {
36353         this.fireEvent('download', this);
36354     },
36355     
36356     loadCanvas : function(src)
36357     {   
36358         if(this.fireEvent('beforeloadcanvas', this, src) != false){
36359             
36360             this.reset();
36361             
36362             this.imageEl = document.createElement('img');
36363             
36364             var _this = this;
36365             
36366             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36367             
36368             this.imageEl.src = src;
36369         }
36370     },
36371     
36372     onLoadCanvas : function()
36373     {   
36374         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36375         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36376         
36377         this.bodyEl.un('click', this.beforeSelectFile, this);
36378         
36379         this.notifyEl.hide();
36380         this.thumbEl.show();
36381         this.footerEl.show();
36382         
36383         this.baseRotateLevel();
36384         
36385         if(this.isDocument){
36386             this.setThumbBoxSize();
36387         }
36388         
36389         this.setThumbBoxPosition();
36390         
36391         this.baseScaleLevel();
36392         
36393         this.draw();
36394         
36395         this.resize();
36396         
36397         this.canvasLoaded = true;
36398         
36399         if(this.loadMask){
36400             this.maskEl.unmask();
36401         }
36402         
36403     },
36404     
36405     setCanvasPosition : function()
36406     {   
36407         if(!this.canvasEl){
36408             return;
36409         }
36410         
36411         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36412         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36413         
36414         this.previewEl.setLeft(pw);
36415         this.previewEl.setTop(ph);
36416         
36417     },
36418     
36419     onMouseDown : function(e)
36420     {   
36421         e.stopEvent();
36422         
36423         this.dragable = true;
36424         this.pinching = false;
36425         
36426         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36427             this.dragable = false;
36428             return;
36429         }
36430         
36431         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36432         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36433         
36434     },
36435     
36436     onMouseMove : function(e)
36437     {   
36438         e.stopEvent();
36439         
36440         if(!this.canvasLoaded){
36441             return;
36442         }
36443         
36444         if (!this.dragable){
36445             return;
36446         }
36447         
36448         var minX = Math.ceil(this.thumbEl.getLeft(true));
36449         var minY = Math.ceil(this.thumbEl.getTop(true));
36450         
36451         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36452         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36453         
36454         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36455         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36456         
36457         x = x - this.mouseX;
36458         y = y - this.mouseY;
36459         
36460         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36461         var bgY = Math.ceil(y + this.previewEl.getTop(true));
36462         
36463         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36464         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36465         
36466         this.previewEl.setLeft(bgX);
36467         this.previewEl.setTop(bgY);
36468         
36469         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36470         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36471     },
36472     
36473     onMouseUp : function(e)
36474     {   
36475         e.stopEvent();
36476         
36477         this.dragable = false;
36478     },
36479     
36480     onMouseWheel : function(e)
36481     {   
36482         e.stopEvent();
36483         
36484         this.startScale = this.scale;
36485         
36486         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36487         
36488         if(!this.zoomable()){
36489             this.scale = this.startScale;
36490             return;
36491         }
36492         
36493         this.draw();
36494         
36495         return;
36496     },
36497     
36498     zoomable : function()
36499     {
36500         var minScale = this.thumbEl.getWidth() / this.minWidth;
36501         
36502         if(this.minWidth < this.minHeight){
36503             minScale = this.thumbEl.getHeight() / this.minHeight;
36504         }
36505         
36506         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36507         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36508         
36509         if(
36510                 this.isDocument &&
36511                 (this.rotate == 0 || this.rotate == 180) && 
36512                 (
36513                     width > this.imageEl.OriginWidth || 
36514                     height > this.imageEl.OriginHeight ||
36515                     (width < this.minWidth && height < this.minHeight)
36516                 )
36517         ){
36518             return false;
36519         }
36520         
36521         if(
36522                 this.isDocument &&
36523                 (this.rotate == 90 || this.rotate == 270) && 
36524                 (
36525                     width > this.imageEl.OriginWidth || 
36526                     height > this.imageEl.OriginHeight ||
36527                     (width < this.minHeight && height < this.minWidth)
36528                 )
36529         ){
36530             return false;
36531         }
36532         
36533         if(
36534                 !this.isDocument &&
36535                 (this.rotate == 0 || this.rotate == 180) && 
36536                 (
36537                     width < this.minWidth || 
36538                     width > this.imageEl.OriginWidth || 
36539                     height < this.minHeight || 
36540                     height > this.imageEl.OriginHeight
36541                 )
36542         ){
36543             return false;
36544         }
36545         
36546         if(
36547                 !this.isDocument &&
36548                 (this.rotate == 90 || this.rotate == 270) && 
36549                 (
36550                     width < this.minHeight || 
36551                     width > this.imageEl.OriginWidth || 
36552                     height < this.minWidth || 
36553                     height > this.imageEl.OriginHeight
36554                 )
36555         ){
36556             return false;
36557         }
36558         
36559         return true;
36560         
36561     },
36562     
36563     onRotateLeft : function(e)
36564     {   
36565         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36566             
36567             var minScale = this.thumbEl.getWidth() / this.minWidth;
36568             
36569             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36570             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36571             
36572             this.startScale = this.scale;
36573             
36574             while (this.getScaleLevel() < minScale){
36575             
36576                 this.scale = this.scale + 1;
36577                 
36578                 if(!this.zoomable()){
36579                     break;
36580                 }
36581                 
36582                 if(
36583                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36584                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36585                 ){
36586                     continue;
36587                 }
36588                 
36589                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36590
36591                 this.draw();
36592                 
36593                 return;
36594             }
36595             
36596             this.scale = this.startScale;
36597             
36598             this.onRotateFail();
36599             
36600             return false;
36601         }
36602         
36603         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36604
36605         if(this.isDocument){
36606             this.setThumbBoxSize();
36607             this.setThumbBoxPosition();
36608             this.setCanvasPosition();
36609         }
36610         
36611         this.draw();
36612         
36613         this.fireEvent('rotate', this, 'left');
36614         
36615     },
36616     
36617     onRotateRight : function(e)
36618     {
36619         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36620             
36621             var minScale = this.thumbEl.getWidth() / this.minWidth;
36622         
36623             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36624             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36625             
36626             this.startScale = this.scale;
36627             
36628             while (this.getScaleLevel() < minScale){
36629             
36630                 this.scale = this.scale + 1;
36631                 
36632                 if(!this.zoomable()){
36633                     break;
36634                 }
36635                 
36636                 if(
36637                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36638                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36639                 ){
36640                     continue;
36641                 }
36642                 
36643                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36644
36645                 this.draw();
36646                 
36647                 return;
36648             }
36649             
36650             this.scale = this.startScale;
36651             
36652             this.onRotateFail();
36653             
36654             return false;
36655         }
36656         
36657         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36658
36659         if(this.isDocument){
36660             this.setThumbBoxSize();
36661             this.setThumbBoxPosition();
36662             this.setCanvasPosition();
36663         }
36664         
36665         this.draw();
36666         
36667         this.fireEvent('rotate', this, 'right');
36668     },
36669     
36670     onRotateFail : function()
36671     {
36672         this.errorEl.show(true);
36673         
36674         var _this = this;
36675         
36676         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36677     },
36678     
36679     draw : function()
36680     {
36681         this.previewEl.dom.innerHTML = '';
36682         
36683         var canvasEl = document.createElement("canvas");
36684         
36685         var contextEl = canvasEl.getContext("2d");
36686         
36687         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36688         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36689         var center = this.imageEl.OriginWidth / 2;
36690         
36691         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36692             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36693             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36694             center = this.imageEl.OriginHeight / 2;
36695         }
36696         
36697         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36698         
36699         contextEl.translate(center, center);
36700         contextEl.rotate(this.rotate * Math.PI / 180);
36701
36702         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36703         
36704         this.canvasEl = document.createElement("canvas");
36705         
36706         this.contextEl = this.canvasEl.getContext("2d");
36707         
36708         switch (this.rotate) {
36709             case 0 :
36710                 
36711                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36712                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36713                 
36714                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36715                 
36716                 break;
36717             case 90 : 
36718                 
36719                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36720                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36721                 
36722                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36723                     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);
36724                     break;
36725                 }
36726                 
36727                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36728                 
36729                 break;
36730             case 180 :
36731                 
36732                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36733                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36734                 
36735                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36736                     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);
36737                     break;
36738                 }
36739                 
36740                 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);
36741                 
36742                 break;
36743             case 270 :
36744                 
36745                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36746                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36747         
36748                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36749                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36750                     break;
36751                 }
36752                 
36753                 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);
36754                 
36755                 break;
36756             default : 
36757                 break;
36758         }
36759         
36760         this.previewEl.appendChild(this.canvasEl);
36761         
36762         this.setCanvasPosition();
36763     },
36764     
36765     crop : function()
36766     {
36767         if(!this.canvasLoaded){
36768             return;
36769         }
36770         
36771         var imageCanvas = document.createElement("canvas");
36772         
36773         var imageContext = imageCanvas.getContext("2d");
36774         
36775         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36776         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36777         
36778         var center = imageCanvas.width / 2;
36779         
36780         imageContext.translate(center, center);
36781         
36782         imageContext.rotate(this.rotate * Math.PI / 180);
36783         
36784         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36785         
36786         var canvas = document.createElement("canvas");
36787         
36788         var context = canvas.getContext("2d");
36789                 
36790         canvas.width = this.minWidth;
36791         canvas.height = this.minHeight;
36792
36793         switch (this.rotate) {
36794             case 0 :
36795                 
36796                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36797                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36798                 
36799                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36800                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36801                 
36802                 var targetWidth = this.minWidth - 2 * x;
36803                 var targetHeight = this.minHeight - 2 * y;
36804                 
36805                 var scale = 1;
36806                 
36807                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36808                     scale = targetWidth / width;
36809                 }
36810                 
36811                 if(x > 0 && y == 0){
36812                     scale = targetHeight / height;
36813                 }
36814                 
36815                 if(x > 0 && y > 0){
36816                     scale = targetWidth / width;
36817                     
36818                     if(width < height){
36819                         scale = targetHeight / height;
36820                     }
36821                 }
36822                 
36823                 context.scale(scale, scale);
36824                 
36825                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36826                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36827
36828                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36829                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36830
36831                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36832                 
36833                 break;
36834             case 90 : 
36835                 
36836                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36837                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36838                 
36839                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36840                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36841                 
36842                 var targetWidth = this.minWidth - 2 * x;
36843                 var targetHeight = this.minHeight - 2 * y;
36844                 
36845                 var scale = 1;
36846                 
36847                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36848                     scale = targetWidth / width;
36849                 }
36850                 
36851                 if(x > 0 && y == 0){
36852                     scale = targetHeight / height;
36853                 }
36854                 
36855                 if(x > 0 && y > 0){
36856                     scale = targetWidth / width;
36857                     
36858                     if(width < height){
36859                         scale = targetHeight / height;
36860                     }
36861                 }
36862                 
36863                 context.scale(scale, scale);
36864                 
36865                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36866                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36867
36868                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36869                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36870                 
36871                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36872                 
36873                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36874                 
36875                 break;
36876             case 180 :
36877                 
36878                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36879                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36880                 
36881                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36882                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36883                 
36884                 var targetWidth = this.minWidth - 2 * x;
36885                 var targetHeight = this.minHeight - 2 * y;
36886                 
36887                 var scale = 1;
36888                 
36889                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36890                     scale = targetWidth / width;
36891                 }
36892                 
36893                 if(x > 0 && y == 0){
36894                     scale = targetHeight / height;
36895                 }
36896                 
36897                 if(x > 0 && y > 0){
36898                     scale = targetWidth / width;
36899                     
36900                     if(width < height){
36901                         scale = targetHeight / height;
36902                     }
36903                 }
36904                 
36905                 context.scale(scale, scale);
36906                 
36907                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36908                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36909
36910                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36911                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36912
36913                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36914                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36915                 
36916                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36917                 
36918                 break;
36919             case 270 :
36920                 
36921                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36922                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36923                 
36924                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36925                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36926                 
36927                 var targetWidth = this.minWidth - 2 * x;
36928                 var targetHeight = this.minHeight - 2 * y;
36929                 
36930                 var scale = 1;
36931                 
36932                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36933                     scale = targetWidth / width;
36934                 }
36935                 
36936                 if(x > 0 && y == 0){
36937                     scale = targetHeight / height;
36938                 }
36939                 
36940                 if(x > 0 && y > 0){
36941                     scale = targetWidth / width;
36942                     
36943                     if(width < height){
36944                         scale = targetHeight / height;
36945                     }
36946                 }
36947                 
36948                 context.scale(scale, scale);
36949                 
36950                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36951                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36952
36953                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36954                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36955                 
36956                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36957                 
36958                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36959                 
36960                 break;
36961             default : 
36962                 break;
36963         }
36964         
36965         this.cropData = canvas.toDataURL(this.cropType);
36966         
36967         if(this.fireEvent('crop', this, this.cropData) !== false){
36968             this.process(this.file, this.cropData);
36969         }
36970         
36971         return;
36972         
36973     },
36974     
36975     setThumbBoxSize : function()
36976     {
36977         var width, height;
36978         
36979         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36980             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36981             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36982             
36983             this.minWidth = width;
36984             this.minHeight = height;
36985             
36986             if(this.rotate == 90 || this.rotate == 270){
36987                 this.minWidth = height;
36988                 this.minHeight = width;
36989             }
36990         }
36991         
36992         height = 300;
36993         width = Math.ceil(this.minWidth * height / this.minHeight);
36994         
36995         if(this.minWidth > this.minHeight){
36996             width = 300;
36997             height = Math.ceil(this.minHeight * width / this.minWidth);
36998         }
36999         
37000         this.thumbEl.setStyle({
37001             width : width + 'px',
37002             height : height + 'px'
37003         });
37004
37005         return;
37006             
37007     },
37008     
37009     setThumbBoxPosition : function()
37010     {
37011         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
37012         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
37013         
37014         this.thumbEl.setLeft(x);
37015         this.thumbEl.setTop(y);
37016         
37017     },
37018     
37019     baseRotateLevel : function()
37020     {
37021         this.baseRotate = 1;
37022         
37023         if(
37024                 typeof(this.exif) != 'undefined' &&
37025                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
37026                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
37027         ){
37028             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
37029         }
37030         
37031         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
37032         
37033     },
37034     
37035     baseScaleLevel : function()
37036     {
37037         var width, height;
37038         
37039         if(this.isDocument){
37040             
37041             if(this.baseRotate == 6 || this.baseRotate == 8){
37042             
37043                 height = this.thumbEl.getHeight();
37044                 this.baseScale = height / this.imageEl.OriginWidth;
37045
37046                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
37047                     width = this.thumbEl.getWidth();
37048                     this.baseScale = width / this.imageEl.OriginHeight;
37049                 }
37050
37051                 return;
37052             }
37053
37054             height = this.thumbEl.getHeight();
37055             this.baseScale = height / this.imageEl.OriginHeight;
37056
37057             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
37058                 width = this.thumbEl.getWidth();
37059                 this.baseScale = width / this.imageEl.OriginWidth;
37060             }
37061
37062             return;
37063         }
37064         
37065         if(this.baseRotate == 6 || this.baseRotate == 8){
37066             
37067             width = this.thumbEl.getHeight();
37068             this.baseScale = width / this.imageEl.OriginHeight;
37069             
37070             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
37071                 height = this.thumbEl.getWidth();
37072                 this.baseScale = height / this.imageEl.OriginHeight;
37073             }
37074             
37075             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
37076                 height = this.thumbEl.getWidth();
37077                 this.baseScale = height / this.imageEl.OriginHeight;
37078                 
37079                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
37080                     width = this.thumbEl.getHeight();
37081                     this.baseScale = width / this.imageEl.OriginWidth;
37082                 }
37083             }
37084             
37085             return;
37086         }
37087         
37088         width = this.thumbEl.getWidth();
37089         this.baseScale = width / this.imageEl.OriginWidth;
37090         
37091         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
37092             height = this.thumbEl.getHeight();
37093             this.baseScale = height / this.imageEl.OriginHeight;
37094         }
37095         
37096         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
37097             
37098             height = this.thumbEl.getHeight();
37099             this.baseScale = height / this.imageEl.OriginHeight;
37100             
37101             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
37102                 width = this.thumbEl.getWidth();
37103                 this.baseScale = width / this.imageEl.OriginWidth;
37104             }
37105             
37106         }
37107         
37108         return;
37109     },
37110     
37111     getScaleLevel : function()
37112     {
37113         return this.baseScale * Math.pow(1.1, this.scale);
37114     },
37115     
37116     onTouchStart : function(e)
37117     {
37118         if(!this.canvasLoaded){
37119             this.beforeSelectFile(e);
37120             return;
37121         }
37122         
37123         var touches = e.browserEvent.touches;
37124         
37125         if(!touches){
37126             return;
37127         }
37128         
37129         if(touches.length == 1){
37130             this.onMouseDown(e);
37131             return;
37132         }
37133         
37134         if(touches.length != 2){
37135             return;
37136         }
37137         
37138         var coords = [];
37139         
37140         for(var i = 0, finger; finger = touches[i]; i++){
37141             coords.push(finger.pageX, finger.pageY);
37142         }
37143         
37144         var x = Math.pow(coords[0] - coords[2], 2);
37145         var y = Math.pow(coords[1] - coords[3], 2);
37146         
37147         this.startDistance = Math.sqrt(x + y);
37148         
37149         this.startScale = this.scale;
37150         
37151         this.pinching = true;
37152         this.dragable = false;
37153         
37154     },
37155     
37156     onTouchMove : function(e)
37157     {
37158         if(!this.pinching && !this.dragable){
37159             return;
37160         }
37161         
37162         var touches = e.browserEvent.touches;
37163         
37164         if(!touches){
37165             return;
37166         }
37167         
37168         if(this.dragable){
37169             this.onMouseMove(e);
37170             return;
37171         }
37172         
37173         var coords = [];
37174         
37175         for(var i = 0, finger; finger = touches[i]; i++){
37176             coords.push(finger.pageX, finger.pageY);
37177         }
37178         
37179         var x = Math.pow(coords[0] - coords[2], 2);
37180         var y = Math.pow(coords[1] - coords[3], 2);
37181         
37182         this.endDistance = Math.sqrt(x + y);
37183         
37184         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
37185         
37186         if(!this.zoomable()){
37187             this.scale = this.startScale;
37188             return;
37189         }
37190         
37191         this.draw();
37192         
37193     },
37194     
37195     onTouchEnd : function(e)
37196     {
37197         this.pinching = false;
37198         this.dragable = false;
37199         
37200     },
37201     
37202     process : function(file, crop)
37203     {
37204         if(this.loadMask){
37205             this.maskEl.mask(this.loadingText);
37206         }
37207         
37208         this.xhr = new XMLHttpRequest();
37209         
37210         file.xhr = this.xhr;
37211
37212         this.xhr.open(this.method, this.url, true);
37213         
37214         var headers = {
37215             "Accept": "application/json",
37216             "Cache-Control": "no-cache",
37217             "X-Requested-With": "XMLHttpRequest"
37218         };
37219         
37220         for (var headerName in headers) {
37221             var headerValue = headers[headerName];
37222             if (headerValue) {
37223                 this.xhr.setRequestHeader(headerName, headerValue);
37224             }
37225         }
37226         
37227         var _this = this;
37228         
37229         this.xhr.onload = function()
37230         {
37231             _this.xhrOnLoad(_this.xhr);
37232         }
37233         
37234         this.xhr.onerror = function()
37235         {
37236             _this.xhrOnError(_this.xhr);
37237         }
37238         
37239         var formData = new FormData();
37240
37241         formData.append('returnHTML', 'NO');
37242         
37243         if(crop){
37244             formData.append('crop', crop);
37245         }
37246         
37247         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
37248             formData.append(this.paramName, file, file.name);
37249         }
37250         
37251         if(typeof(file.filename) != 'undefined'){
37252             formData.append('filename', file.filename);
37253         }
37254         
37255         if(typeof(file.mimetype) != 'undefined'){
37256             formData.append('mimetype', file.mimetype);
37257         }
37258         
37259         if(this.fireEvent('arrange', this, formData) != false){
37260             this.xhr.send(formData);
37261         };
37262     },
37263     
37264     xhrOnLoad : function(xhr)
37265     {
37266         if(this.loadMask){
37267             this.maskEl.unmask();
37268         }
37269         
37270         if (xhr.readyState !== 4) {
37271             this.fireEvent('exception', this, xhr);
37272             return;
37273         }
37274
37275         var response = Roo.decode(xhr.responseText);
37276         
37277         if(!response.success){
37278             this.fireEvent('exception', this, xhr);
37279             return;
37280         }
37281         
37282         var response = Roo.decode(xhr.responseText);
37283         
37284         this.fireEvent('upload', this, response);
37285         
37286     },
37287     
37288     xhrOnError : function()
37289     {
37290         if(this.loadMask){
37291             this.maskEl.unmask();
37292         }
37293         
37294         Roo.log('xhr on error');
37295         
37296         var response = Roo.decode(xhr.responseText);
37297           
37298         Roo.log(response);
37299         
37300     },
37301     
37302     prepare : function(file)
37303     {   
37304         if(this.loadMask){
37305             this.maskEl.mask(this.loadingText);
37306         }
37307         
37308         this.file = false;
37309         this.exif = {};
37310         
37311         if(typeof(file) === 'string'){
37312             this.loadCanvas(file);
37313             return;
37314         }
37315         
37316         if(!file || !this.urlAPI){
37317             return;
37318         }
37319         
37320         this.file = file;
37321         this.cropType = file.type;
37322         
37323         var _this = this;
37324         
37325         if(this.fireEvent('prepare', this, this.file) != false){
37326             
37327             var reader = new FileReader();
37328             
37329             reader.onload = function (e) {
37330                 if (e.target.error) {
37331                     Roo.log(e.target.error);
37332                     return;
37333                 }
37334                 
37335                 var buffer = e.target.result,
37336                     dataView = new DataView(buffer),
37337                     offset = 2,
37338                     maxOffset = dataView.byteLength - 4,
37339                     markerBytes,
37340                     markerLength;
37341                 
37342                 if (dataView.getUint16(0) === 0xffd8) {
37343                     while (offset < maxOffset) {
37344                         markerBytes = dataView.getUint16(offset);
37345                         
37346                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37347                             markerLength = dataView.getUint16(offset + 2) + 2;
37348                             if (offset + markerLength > dataView.byteLength) {
37349                                 Roo.log('Invalid meta data: Invalid segment size.');
37350                                 break;
37351                             }
37352                             
37353                             if(markerBytes == 0xffe1){
37354                                 _this.parseExifData(
37355                                     dataView,
37356                                     offset,
37357                                     markerLength
37358                                 );
37359                             }
37360                             
37361                             offset += markerLength;
37362                             
37363                             continue;
37364                         }
37365                         
37366                         break;
37367                     }
37368                     
37369                 }
37370                 
37371                 var url = _this.urlAPI.createObjectURL(_this.file);
37372                 
37373                 _this.loadCanvas(url);
37374                 
37375                 return;
37376             }
37377             
37378             reader.readAsArrayBuffer(this.file);
37379             
37380         }
37381         
37382     },
37383     
37384     parseExifData : function(dataView, offset, length)
37385     {
37386         var tiffOffset = offset + 10,
37387             littleEndian,
37388             dirOffset;
37389     
37390         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37391             // No Exif data, might be XMP data instead
37392             return;
37393         }
37394         
37395         // Check for the ASCII code for "Exif" (0x45786966):
37396         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37397             // No Exif data, might be XMP data instead
37398             return;
37399         }
37400         if (tiffOffset + 8 > dataView.byteLength) {
37401             Roo.log('Invalid Exif data: Invalid segment size.');
37402             return;
37403         }
37404         // Check for the two null bytes:
37405         if (dataView.getUint16(offset + 8) !== 0x0000) {
37406             Roo.log('Invalid Exif data: Missing byte alignment offset.');
37407             return;
37408         }
37409         // Check the byte alignment:
37410         switch (dataView.getUint16(tiffOffset)) {
37411         case 0x4949:
37412             littleEndian = true;
37413             break;
37414         case 0x4D4D:
37415             littleEndian = false;
37416             break;
37417         default:
37418             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37419             return;
37420         }
37421         // Check for the TIFF tag marker (0x002A):
37422         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37423             Roo.log('Invalid Exif data: Missing TIFF marker.');
37424             return;
37425         }
37426         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37427         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37428         
37429         this.parseExifTags(
37430             dataView,
37431             tiffOffset,
37432             tiffOffset + dirOffset,
37433             littleEndian
37434         );
37435     },
37436     
37437     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37438     {
37439         var tagsNumber,
37440             dirEndOffset,
37441             i;
37442         if (dirOffset + 6 > dataView.byteLength) {
37443             Roo.log('Invalid Exif data: Invalid directory offset.');
37444             return;
37445         }
37446         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37447         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37448         if (dirEndOffset + 4 > dataView.byteLength) {
37449             Roo.log('Invalid Exif data: Invalid directory size.');
37450             return;
37451         }
37452         for (i = 0; i < tagsNumber; i += 1) {
37453             this.parseExifTag(
37454                 dataView,
37455                 tiffOffset,
37456                 dirOffset + 2 + 12 * i, // tag offset
37457                 littleEndian
37458             );
37459         }
37460         // Return the offset to the next directory:
37461         return dataView.getUint32(dirEndOffset, littleEndian);
37462     },
37463     
37464     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
37465     {
37466         var tag = dataView.getUint16(offset, littleEndian);
37467         
37468         this.exif[tag] = this.getExifValue(
37469             dataView,
37470             tiffOffset,
37471             offset,
37472             dataView.getUint16(offset + 2, littleEndian), // tag type
37473             dataView.getUint32(offset + 4, littleEndian), // tag length
37474             littleEndian
37475         );
37476     },
37477     
37478     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37479     {
37480         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37481             tagSize,
37482             dataOffset,
37483             values,
37484             i,
37485             str,
37486             c;
37487     
37488         if (!tagType) {
37489             Roo.log('Invalid Exif data: Invalid tag type.');
37490             return;
37491         }
37492         
37493         tagSize = tagType.size * length;
37494         // Determine if the value is contained in the dataOffset bytes,
37495         // or if the value at the dataOffset is a pointer to the actual data:
37496         dataOffset = tagSize > 4 ?
37497                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37498         if (dataOffset + tagSize > dataView.byteLength) {
37499             Roo.log('Invalid Exif data: Invalid data offset.');
37500             return;
37501         }
37502         if (length === 1) {
37503             return tagType.getValue(dataView, dataOffset, littleEndian);
37504         }
37505         values = [];
37506         for (i = 0; i < length; i += 1) {
37507             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37508         }
37509         
37510         if (tagType.ascii) {
37511             str = '';
37512             // Concatenate the chars:
37513             for (i = 0; i < values.length; i += 1) {
37514                 c = values[i];
37515                 // Ignore the terminating NULL byte(s):
37516                 if (c === '\u0000') {
37517                     break;
37518                 }
37519                 str += c;
37520             }
37521             return str;
37522         }
37523         return values;
37524     }
37525     
37526 });
37527
37528 Roo.apply(Roo.bootstrap.UploadCropbox, {
37529     tags : {
37530         'Orientation': 0x0112
37531     },
37532     
37533     Orientation: {
37534             1: 0, //'top-left',
37535 //            2: 'top-right',
37536             3: 180, //'bottom-right',
37537 //            4: 'bottom-left',
37538 //            5: 'left-top',
37539             6: 90, //'right-top',
37540 //            7: 'right-bottom',
37541             8: 270 //'left-bottom'
37542     },
37543     
37544     exifTagTypes : {
37545         // byte, 8-bit unsigned int:
37546         1: {
37547             getValue: function (dataView, dataOffset) {
37548                 return dataView.getUint8(dataOffset);
37549             },
37550             size: 1
37551         },
37552         // ascii, 8-bit byte:
37553         2: {
37554             getValue: function (dataView, dataOffset) {
37555                 return String.fromCharCode(dataView.getUint8(dataOffset));
37556             },
37557             size: 1,
37558             ascii: true
37559         },
37560         // short, 16 bit int:
37561         3: {
37562             getValue: function (dataView, dataOffset, littleEndian) {
37563                 return dataView.getUint16(dataOffset, littleEndian);
37564             },
37565             size: 2
37566         },
37567         // long, 32 bit int:
37568         4: {
37569             getValue: function (dataView, dataOffset, littleEndian) {
37570                 return dataView.getUint32(dataOffset, littleEndian);
37571             },
37572             size: 4
37573         },
37574         // rational = two long values, first is numerator, second is denominator:
37575         5: {
37576             getValue: function (dataView, dataOffset, littleEndian) {
37577                 return dataView.getUint32(dataOffset, littleEndian) /
37578                     dataView.getUint32(dataOffset + 4, littleEndian);
37579             },
37580             size: 8
37581         },
37582         // slong, 32 bit signed int:
37583         9: {
37584             getValue: function (dataView, dataOffset, littleEndian) {
37585                 return dataView.getInt32(dataOffset, littleEndian);
37586             },
37587             size: 4
37588         },
37589         // srational, two slongs, first is numerator, second is denominator:
37590         10: {
37591             getValue: function (dataView, dataOffset, littleEndian) {
37592                 return dataView.getInt32(dataOffset, littleEndian) /
37593                     dataView.getInt32(dataOffset + 4, littleEndian);
37594             },
37595             size: 8
37596         }
37597     },
37598     
37599     footer : {
37600         STANDARD : [
37601             {
37602                 tag : 'div',
37603                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37604                 action : 'rotate-left',
37605                 cn : [
37606                     {
37607                         tag : 'button',
37608                         cls : 'btn btn-default',
37609                         html : '<i class="fa fa-undo"></i>'
37610                     }
37611                 ]
37612             },
37613             {
37614                 tag : 'div',
37615                 cls : 'btn-group roo-upload-cropbox-picture',
37616                 action : 'picture',
37617                 cn : [
37618                     {
37619                         tag : 'button',
37620                         cls : 'btn btn-default',
37621                         html : '<i class="fa fa-picture-o"></i>'
37622                     }
37623                 ]
37624             },
37625             {
37626                 tag : 'div',
37627                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37628                 action : 'rotate-right',
37629                 cn : [
37630                     {
37631                         tag : 'button',
37632                         cls : 'btn btn-default',
37633                         html : '<i class="fa fa-repeat"></i>'
37634                     }
37635                 ]
37636             }
37637         ],
37638         DOCUMENT : [
37639             {
37640                 tag : 'div',
37641                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37642                 action : 'rotate-left',
37643                 cn : [
37644                     {
37645                         tag : 'button',
37646                         cls : 'btn btn-default',
37647                         html : '<i class="fa fa-undo"></i>'
37648                     }
37649                 ]
37650             },
37651             {
37652                 tag : 'div',
37653                 cls : 'btn-group roo-upload-cropbox-download',
37654                 action : 'download',
37655                 cn : [
37656                     {
37657                         tag : 'button',
37658                         cls : 'btn btn-default',
37659                         html : '<i class="fa fa-download"></i>'
37660                     }
37661                 ]
37662             },
37663             {
37664                 tag : 'div',
37665                 cls : 'btn-group roo-upload-cropbox-crop',
37666                 action : 'crop',
37667                 cn : [
37668                     {
37669                         tag : 'button',
37670                         cls : 'btn btn-default',
37671                         html : '<i class="fa fa-crop"></i>'
37672                     }
37673                 ]
37674             },
37675             {
37676                 tag : 'div',
37677                 cls : 'btn-group roo-upload-cropbox-trash',
37678                 action : 'trash',
37679                 cn : [
37680                     {
37681                         tag : 'button',
37682                         cls : 'btn btn-default',
37683                         html : '<i class="fa fa-trash"></i>'
37684                     }
37685                 ]
37686             },
37687             {
37688                 tag : 'div',
37689                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37690                 action : 'rotate-right',
37691                 cn : [
37692                     {
37693                         tag : 'button',
37694                         cls : 'btn btn-default',
37695                         html : '<i class="fa fa-repeat"></i>'
37696                     }
37697                 ]
37698             }
37699         ],
37700         ROTATOR : [
37701             {
37702                 tag : 'div',
37703                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37704                 action : 'rotate-left',
37705                 cn : [
37706                     {
37707                         tag : 'button',
37708                         cls : 'btn btn-default',
37709                         html : '<i class="fa fa-undo"></i>'
37710                     }
37711                 ]
37712             },
37713             {
37714                 tag : 'div',
37715                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37716                 action : 'rotate-right',
37717                 cn : [
37718                     {
37719                         tag : 'button',
37720                         cls : 'btn btn-default',
37721                         html : '<i class="fa fa-repeat"></i>'
37722                     }
37723                 ]
37724             }
37725         ]
37726     }
37727 });
37728
37729 /*
37730 * Licence: LGPL
37731 */
37732
37733 /**
37734  * @class Roo.bootstrap.DocumentManager
37735  * @extends Roo.bootstrap.Component
37736  * Bootstrap DocumentManager class
37737  * @cfg {String} paramName default 'imageUpload'
37738  * @cfg {String} toolTipName default 'filename'
37739  * @cfg {String} method default POST
37740  * @cfg {String} url action url
37741  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37742  * @cfg {Boolean} multiple multiple upload default true
37743  * @cfg {Number} thumbSize default 300
37744  * @cfg {String} fieldLabel
37745  * @cfg {Number} labelWidth default 4
37746  * @cfg {String} labelAlign (left|top) default left
37747  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37748 * @cfg {Number} labellg set the width of label (1-12)
37749  * @cfg {Number} labelmd set the width of label (1-12)
37750  * @cfg {Number} labelsm set the width of label (1-12)
37751  * @cfg {Number} labelxs set the width of label (1-12)
37752  * 
37753  * @constructor
37754  * Create a new DocumentManager
37755  * @param {Object} config The config object
37756  */
37757
37758 Roo.bootstrap.DocumentManager = function(config){
37759     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37760     
37761     this.files = [];
37762     this.delegates = [];
37763     
37764     this.addEvents({
37765         /**
37766          * @event initial
37767          * Fire when initial the DocumentManager
37768          * @param {Roo.bootstrap.DocumentManager} this
37769          */
37770         "initial" : true,
37771         /**
37772          * @event inspect
37773          * inspect selected file
37774          * @param {Roo.bootstrap.DocumentManager} this
37775          * @param {File} file
37776          */
37777         "inspect" : true,
37778         /**
37779          * @event exception
37780          * Fire when xhr load exception
37781          * @param {Roo.bootstrap.DocumentManager} this
37782          * @param {XMLHttpRequest} xhr
37783          */
37784         "exception" : true,
37785         /**
37786          * @event afterupload
37787          * Fire when xhr load exception
37788          * @param {Roo.bootstrap.DocumentManager} this
37789          * @param {XMLHttpRequest} xhr
37790          */
37791         "afterupload" : true,
37792         /**
37793          * @event prepare
37794          * prepare the form data
37795          * @param {Roo.bootstrap.DocumentManager} this
37796          * @param {Object} formData
37797          */
37798         "prepare" : true,
37799         /**
37800          * @event remove
37801          * Fire when remove the file
37802          * @param {Roo.bootstrap.DocumentManager} this
37803          * @param {Object} file
37804          */
37805         "remove" : true,
37806         /**
37807          * @event refresh
37808          * Fire after refresh the file
37809          * @param {Roo.bootstrap.DocumentManager} this
37810          */
37811         "refresh" : true,
37812         /**
37813          * @event click
37814          * Fire after click the image
37815          * @param {Roo.bootstrap.DocumentManager} this
37816          * @param {Object} file
37817          */
37818         "click" : true,
37819         /**
37820          * @event edit
37821          * Fire when upload a image and editable set to true
37822          * @param {Roo.bootstrap.DocumentManager} this
37823          * @param {Object} file
37824          */
37825         "edit" : true,
37826         /**
37827          * @event beforeselectfile
37828          * Fire before select file
37829          * @param {Roo.bootstrap.DocumentManager} this
37830          */
37831         "beforeselectfile" : true,
37832         /**
37833          * @event process
37834          * Fire before process file
37835          * @param {Roo.bootstrap.DocumentManager} this
37836          * @param {Object} file
37837          */
37838         "process" : true,
37839         /**
37840          * @event previewrendered
37841          * Fire when preview rendered
37842          * @param {Roo.bootstrap.DocumentManager} this
37843          * @param {Object} file
37844          */
37845         "previewrendered" : true,
37846         /**
37847          */
37848         "previewResize" : true
37849         
37850     });
37851 };
37852
37853 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37854     
37855     boxes : 0,
37856     inputName : '',
37857     thumbSize : 300,
37858     multiple : true,
37859     files : false,
37860     method : 'POST',
37861     url : '',
37862     paramName : 'imageUpload',
37863     toolTipName : 'filename',
37864     fieldLabel : '',
37865     labelWidth : 4,
37866     labelAlign : 'left',
37867     editable : true,
37868     delegates : false,
37869     xhr : false, 
37870     
37871     labellg : 0,
37872     labelmd : 0,
37873     labelsm : 0,
37874     labelxs : 0,
37875     
37876     getAutoCreate : function()
37877     {   
37878         var managerWidget = {
37879             tag : 'div',
37880             cls : 'roo-document-manager',
37881             cn : [
37882                 {
37883                     tag : 'input',
37884                     cls : 'roo-document-manager-selector',
37885                     type : 'file'
37886                 },
37887                 {
37888                     tag : 'div',
37889                     cls : 'roo-document-manager-uploader',
37890                     cn : [
37891                         {
37892                             tag : 'div',
37893                             cls : 'roo-document-manager-upload-btn',
37894                             html : '<i class="fa fa-plus"></i>'
37895                         }
37896                     ]
37897                     
37898                 }
37899             ]
37900         };
37901         
37902         var content = [
37903             {
37904                 tag : 'div',
37905                 cls : 'column col-md-12',
37906                 cn : managerWidget
37907             }
37908         ];
37909         
37910         if(this.fieldLabel.length){
37911             
37912             content = [
37913                 {
37914                     tag : 'div',
37915                     cls : 'column col-md-12',
37916                     html : this.fieldLabel
37917                 },
37918                 {
37919                     tag : 'div',
37920                     cls : 'column col-md-12',
37921                     cn : managerWidget
37922                 }
37923             ];
37924
37925             if(this.labelAlign == 'left'){
37926                 content = [
37927                     {
37928                         tag : 'div',
37929                         cls : 'column',
37930                         html : this.fieldLabel
37931                     },
37932                     {
37933                         tag : 'div',
37934                         cls : 'column',
37935                         cn : managerWidget
37936                     }
37937                 ];
37938                 
37939                 if(this.labelWidth > 12){
37940                     content[0].style = "width: " + this.labelWidth + 'px';
37941                 }
37942
37943                 if(this.labelWidth < 13 && this.labelmd == 0){
37944                     this.labelmd = this.labelWidth;
37945                 }
37946
37947                 if(this.labellg > 0){
37948                     content[0].cls += ' col-lg-' + this.labellg;
37949                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37950                 }
37951
37952                 if(this.labelmd > 0){
37953                     content[0].cls += ' col-md-' + this.labelmd;
37954                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37955                 }
37956
37957                 if(this.labelsm > 0){
37958                     content[0].cls += ' col-sm-' + this.labelsm;
37959                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37960                 }
37961
37962                 if(this.labelxs > 0){
37963                     content[0].cls += ' col-xs-' + this.labelxs;
37964                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37965                 }
37966                 
37967             }
37968         }
37969         
37970         var cfg = {
37971             tag : 'div',
37972             cls : 'row clearfix',
37973             cn : content
37974         };
37975         
37976         return cfg;
37977         
37978     },
37979     
37980     initEvents : function()
37981     {
37982         this.managerEl = this.el.select('.roo-document-manager', true).first();
37983         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37984         
37985         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37986         this.selectorEl.hide();
37987         
37988         if(this.multiple){
37989             this.selectorEl.attr('multiple', 'multiple');
37990         }
37991         
37992         this.selectorEl.on('change', this.onFileSelected, this);
37993         
37994         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37995         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37996         
37997         this.uploader.on('click', this.onUploaderClick, this);
37998         
37999         this.renderProgressDialog();
38000         
38001         var _this = this;
38002         
38003         window.addEventListener("resize", function() { _this.refresh(); } );
38004         
38005         this.fireEvent('initial', this);
38006     },
38007     
38008     renderProgressDialog : function()
38009     {
38010         var _this = this;
38011         
38012         this.progressDialog = new Roo.bootstrap.Modal({
38013             cls : 'roo-document-manager-progress-dialog',
38014             allow_close : false,
38015             animate : false,
38016             title : '',
38017             buttons : [
38018                 {
38019                     name  :'cancel',
38020                     weight : 'danger',
38021                     html : 'Cancel'
38022                 }
38023             ], 
38024             listeners : { 
38025                 btnclick : function() {
38026                     _this.uploadCancel();
38027                     this.hide();
38028                 }
38029             }
38030         });
38031          
38032         this.progressDialog.render(Roo.get(document.body));
38033          
38034         this.progress = new Roo.bootstrap.Progress({
38035             cls : 'roo-document-manager-progress',
38036             active : true,
38037             striped : true
38038         });
38039         
38040         this.progress.render(this.progressDialog.getChildContainer());
38041         
38042         this.progressBar = new Roo.bootstrap.ProgressBar({
38043             cls : 'roo-document-manager-progress-bar',
38044             aria_valuenow : 0,
38045             aria_valuemin : 0,
38046             aria_valuemax : 12,
38047             panel : 'success'
38048         });
38049         
38050         this.progressBar.render(this.progress.getChildContainer());
38051     },
38052     
38053     onUploaderClick : function(e)
38054     {
38055         e.preventDefault();
38056      
38057         if(this.fireEvent('beforeselectfile', this) != false){
38058             this.selectorEl.dom.click();
38059         }
38060         
38061     },
38062     
38063     onFileSelected : function(e)
38064     {
38065         e.preventDefault();
38066         
38067         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
38068             return;
38069         }
38070         
38071         Roo.each(this.selectorEl.dom.files, function(file){
38072             if(this.fireEvent('inspect', this, file) != false){
38073                 this.files.push(file);
38074             }
38075         }, this);
38076         
38077         this.queue();
38078         
38079     },
38080     
38081     queue : function()
38082     {
38083         this.selectorEl.dom.value = '';
38084         
38085         if(!this.files || !this.files.length){
38086             return;
38087         }
38088         
38089         if(this.boxes > 0 && this.files.length > this.boxes){
38090             this.files = this.files.slice(0, this.boxes);
38091         }
38092         
38093         this.uploader.show();
38094         
38095         if(this.boxes > 0 && this.files.length > this.boxes - 1){
38096             this.uploader.hide();
38097         }
38098         
38099         var _this = this;
38100         
38101         var files = [];
38102         
38103         var docs = [];
38104         
38105         Roo.each(this.files, function(file){
38106             
38107             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
38108                 var f = this.renderPreview(file);
38109                 files.push(f);
38110                 return;
38111             }
38112             
38113             if(file.type.indexOf('image') != -1){
38114                 this.delegates.push(
38115                     (function(){
38116                         _this.process(file);
38117                     }).createDelegate(this)
38118                 );
38119         
38120                 return;
38121             }
38122             
38123             docs.push(
38124                 (function(){
38125                     _this.process(file);
38126                 }).createDelegate(this)
38127             );
38128             
38129         }, this);
38130         
38131         this.files = files;
38132         
38133         this.delegates = this.delegates.concat(docs);
38134         
38135         if(!this.delegates.length){
38136             this.refresh();
38137             return;
38138         }
38139         
38140         this.progressBar.aria_valuemax = this.delegates.length;
38141         
38142         this.arrange();
38143         
38144         return;
38145     },
38146     
38147     arrange : function()
38148     {
38149         if(!this.delegates.length){
38150             this.progressDialog.hide();
38151             this.refresh();
38152             return;
38153         }
38154         
38155         var delegate = this.delegates.shift();
38156         
38157         this.progressDialog.show();
38158         
38159         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
38160         
38161         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
38162         
38163         delegate();
38164     },
38165     
38166     refresh : function()
38167     {
38168         this.uploader.show();
38169         
38170         if(this.boxes > 0 && this.files.length > this.boxes - 1){
38171             this.uploader.hide();
38172         }
38173         
38174         Roo.isTouch ? this.closable(false) : this.closable(true);
38175         
38176         this.fireEvent('refresh', this);
38177     },
38178     
38179     onRemove : function(e, el, o)
38180     {
38181         e.preventDefault();
38182         
38183         this.fireEvent('remove', this, o);
38184         
38185     },
38186     
38187     remove : function(o)
38188     {
38189         var files = [];
38190         
38191         Roo.each(this.files, function(file){
38192             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
38193                 files.push(file);
38194                 return;
38195             }
38196
38197             o.target.remove();
38198
38199         }, this);
38200         
38201         this.files = files;
38202         
38203         this.refresh();
38204     },
38205     
38206     clear : function()
38207     {
38208         Roo.each(this.files, function(file){
38209             if(!file.target){
38210                 return;
38211             }
38212             
38213             file.target.remove();
38214
38215         }, this);
38216         
38217         this.files = [];
38218         
38219         this.refresh();
38220     },
38221     
38222     onClick : function(e, el, o)
38223     {
38224         e.preventDefault();
38225         
38226         this.fireEvent('click', this, o);
38227         
38228     },
38229     
38230     closable : function(closable)
38231     {
38232         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
38233             
38234             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
38235             
38236             if(closable){
38237                 el.show();
38238                 return;
38239             }
38240             
38241             el.hide();
38242             
38243         }, this);
38244     },
38245     
38246     xhrOnLoad : function(xhr)
38247     {
38248         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38249             el.remove();
38250         }, this);
38251         
38252         if (xhr.readyState !== 4) {
38253             this.arrange();
38254             this.fireEvent('exception', this, xhr);
38255             return;
38256         }
38257
38258         var response = Roo.decode(xhr.responseText);
38259         
38260         if(!response.success){
38261             this.arrange();
38262             this.fireEvent('exception', this, xhr);
38263             return;
38264         }
38265         
38266         var file = this.renderPreview(response.data);
38267         
38268         this.files.push(file);
38269         
38270         this.arrange();
38271         
38272         this.fireEvent('afterupload', this, xhr);
38273         
38274     },
38275     
38276     xhrOnError : function(xhr)
38277     {
38278         Roo.log('xhr on error');
38279         
38280         var response = Roo.decode(xhr.responseText);
38281           
38282         Roo.log(response);
38283         
38284         this.arrange();
38285     },
38286     
38287     process : function(file)
38288     {
38289         if(this.fireEvent('process', this, file) !== false){
38290             if(this.editable && file.type.indexOf('image') != -1){
38291                 this.fireEvent('edit', this, file);
38292                 return;
38293             }
38294
38295             this.uploadStart(file, false);
38296
38297             return;
38298         }
38299         
38300     },
38301     
38302     uploadStart : function(file, crop)
38303     {
38304         this.xhr = new XMLHttpRequest();
38305         
38306         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
38307             this.arrange();
38308             return;
38309         }
38310         
38311         file.xhr = this.xhr;
38312             
38313         this.managerEl.createChild({
38314             tag : 'div',
38315             cls : 'roo-document-manager-loading',
38316             cn : [
38317                 {
38318                     tag : 'div',
38319                     tooltip : file.name,
38320                     cls : 'roo-document-manager-thumb',
38321                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38322                 }
38323             ]
38324
38325         });
38326
38327         this.xhr.open(this.method, this.url, true);
38328         
38329         var headers = {
38330             "Accept": "application/json",
38331             "Cache-Control": "no-cache",
38332             "X-Requested-With": "XMLHttpRequest"
38333         };
38334         
38335         for (var headerName in headers) {
38336             var headerValue = headers[headerName];
38337             if (headerValue) {
38338                 this.xhr.setRequestHeader(headerName, headerValue);
38339             }
38340         }
38341         
38342         var _this = this;
38343         
38344         this.xhr.onload = function()
38345         {
38346             _this.xhrOnLoad(_this.xhr);
38347         }
38348         
38349         this.xhr.onerror = function()
38350         {
38351             _this.xhrOnError(_this.xhr);
38352         }
38353         
38354         var formData = new FormData();
38355
38356         formData.append('returnHTML', 'NO');
38357         
38358         if(crop){
38359             formData.append('crop', crop);
38360         }
38361         
38362         formData.append(this.paramName, file, file.name);
38363         
38364         var options = {
38365             file : file, 
38366             manually : false
38367         };
38368         
38369         if(this.fireEvent('prepare', this, formData, options) != false){
38370             
38371             if(options.manually){
38372                 return;
38373             }
38374             
38375             this.xhr.send(formData);
38376             return;
38377         };
38378         
38379         this.uploadCancel();
38380     },
38381     
38382     uploadCancel : function()
38383     {
38384         if (this.xhr) {
38385             this.xhr.abort();
38386         }
38387         
38388         this.delegates = [];
38389         
38390         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38391             el.remove();
38392         }, this);
38393         
38394         this.arrange();
38395     },
38396     
38397     renderPreview : function(file)
38398     {
38399         if(typeof(file.target) != 'undefined' && file.target){
38400             return file;
38401         }
38402         
38403         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38404         
38405         var previewEl = this.managerEl.createChild({
38406             tag : 'div',
38407             cls : 'roo-document-manager-preview',
38408             cn : [
38409                 {
38410                     tag : 'div',
38411                     tooltip : file[this.toolTipName],
38412                     cls : 'roo-document-manager-thumb',
38413                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38414                 },
38415                 {
38416                     tag : 'button',
38417                     cls : 'close',
38418                     html : '<i class="fa fa-times-circle"></i>'
38419                 }
38420             ]
38421         });
38422
38423         var close = previewEl.select('button.close', true).first();
38424
38425         close.on('click', this.onRemove, this, file);
38426
38427         file.target = previewEl;
38428
38429         var image = previewEl.select('img', true).first();
38430         
38431         var _this = this;
38432         
38433         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38434         
38435         image.on('click', this.onClick, this, file);
38436         
38437         this.fireEvent('previewrendered', this, file);
38438         
38439         return file;
38440         
38441     },
38442     
38443     onPreviewLoad : function(file, image)
38444     {
38445         if(typeof(file.target) == 'undefined' || !file.target){
38446             return;
38447         }
38448         
38449         var width = image.dom.naturalWidth || image.dom.width;
38450         var height = image.dom.naturalHeight || image.dom.height;
38451         
38452         if(!this.previewResize) {
38453             return;
38454         }
38455         
38456         if(width > height){
38457             file.target.addClass('wide');
38458             return;
38459         }
38460         
38461         file.target.addClass('tall');
38462         return;
38463         
38464     },
38465     
38466     uploadFromSource : function(file, crop)
38467     {
38468         this.xhr = new XMLHttpRequest();
38469         
38470         this.managerEl.createChild({
38471             tag : 'div',
38472             cls : 'roo-document-manager-loading',
38473             cn : [
38474                 {
38475                     tag : 'div',
38476                     tooltip : file.name,
38477                     cls : 'roo-document-manager-thumb',
38478                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38479                 }
38480             ]
38481
38482         });
38483
38484         this.xhr.open(this.method, this.url, true);
38485         
38486         var headers = {
38487             "Accept": "application/json",
38488             "Cache-Control": "no-cache",
38489             "X-Requested-With": "XMLHttpRequest"
38490         };
38491         
38492         for (var headerName in headers) {
38493             var headerValue = headers[headerName];
38494             if (headerValue) {
38495                 this.xhr.setRequestHeader(headerName, headerValue);
38496             }
38497         }
38498         
38499         var _this = this;
38500         
38501         this.xhr.onload = function()
38502         {
38503             _this.xhrOnLoad(_this.xhr);
38504         }
38505         
38506         this.xhr.onerror = function()
38507         {
38508             _this.xhrOnError(_this.xhr);
38509         }
38510         
38511         var formData = new FormData();
38512
38513         formData.append('returnHTML', 'NO');
38514         
38515         formData.append('crop', crop);
38516         
38517         if(typeof(file.filename) != 'undefined'){
38518             formData.append('filename', file.filename);
38519         }
38520         
38521         if(typeof(file.mimetype) != 'undefined'){
38522             formData.append('mimetype', file.mimetype);
38523         }
38524         
38525         Roo.log(formData);
38526         
38527         if(this.fireEvent('prepare', this, formData) != false){
38528             this.xhr.send(formData);
38529         };
38530     }
38531 });
38532
38533 /*
38534 * Licence: LGPL
38535 */
38536
38537 /**
38538  * @class Roo.bootstrap.DocumentViewer
38539  * @extends Roo.bootstrap.Component
38540  * Bootstrap DocumentViewer class
38541  * @cfg {Boolean} showDownload (true|false) show download button (default true)
38542  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38543  * 
38544  * @constructor
38545  * Create a new DocumentViewer
38546  * @param {Object} config The config object
38547  */
38548
38549 Roo.bootstrap.DocumentViewer = function(config){
38550     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38551     
38552     this.addEvents({
38553         /**
38554          * @event initial
38555          * Fire after initEvent
38556          * @param {Roo.bootstrap.DocumentViewer} this
38557          */
38558         "initial" : true,
38559         /**
38560          * @event click
38561          * Fire after click
38562          * @param {Roo.bootstrap.DocumentViewer} this
38563          */
38564         "click" : true,
38565         /**
38566          * @event download
38567          * Fire after download button
38568          * @param {Roo.bootstrap.DocumentViewer} this
38569          */
38570         "download" : true,
38571         /**
38572          * @event trash
38573          * Fire after trash button
38574          * @param {Roo.bootstrap.DocumentViewer} this
38575          */
38576         "trash" : true
38577         
38578     });
38579 };
38580
38581 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
38582     
38583     showDownload : true,
38584     
38585     showTrash : true,
38586     
38587     getAutoCreate : function()
38588     {
38589         var cfg = {
38590             tag : 'div',
38591             cls : 'roo-document-viewer',
38592             cn : [
38593                 {
38594                     tag : 'div',
38595                     cls : 'roo-document-viewer-body',
38596                     cn : [
38597                         {
38598                             tag : 'div',
38599                             cls : 'roo-document-viewer-thumb',
38600                             cn : [
38601                                 {
38602                                     tag : 'img',
38603                                     cls : 'roo-document-viewer-image'
38604                                 }
38605                             ]
38606                         }
38607                     ]
38608                 },
38609                 {
38610                     tag : 'div',
38611                     cls : 'roo-document-viewer-footer',
38612                     cn : {
38613                         tag : 'div',
38614                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38615                         cn : [
38616                             {
38617                                 tag : 'div',
38618                                 cls : 'btn-group roo-document-viewer-download',
38619                                 cn : [
38620                                     {
38621                                         tag : 'button',
38622                                         cls : 'btn btn-default',
38623                                         html : '<i class="fa fa-download"></i>'
38624                                     }
38625                                 ]
38626                             },
38627                             {
38628                                 tag : 'div',
38629                                 cls : 'btn-group roo-document-viewer-trash',
38630                                 cn : [
38631                                     {
38632                                         tag : 'button',
38633                                         cls : 'btn btn-default',
38634                                         html : '<i class="fa fa-trash"></i>'
38635                                     }
38636                                 ]
38637                             }
38638                         ]
38639                     }
38640                 }
38641             ]
38642         };
38643         
38644         return cfg;
38645     },
38646     
38647     initEvents : function()
38648     {
38649         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38650         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38651         
38652         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38653         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38654         
38655         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38656         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38657         
38658         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38659         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38660         
38661         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38662         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38663         
38664         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38665         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38666         
38667         this.bodyEl.on('click', this.onClick, this);
38668         this.downloadBtn.on('click', this.onDownload, this);
38669         this.trashBtn.on('click', this.onTrash, this);
38670         
38671         this.downloadBtn.hide();
38672         this.trashBtn.hide();
38673         
38674         if(this.showDownload){
38675             this.downloadBtn.show();
38676         }
38677         
38678         if(this.showTrash){
38679             this.trashBtn.show();
38680         }
38681         
38682         if(!this.showDownload && !this.showTrash) {
38683             this.footerEl.hide();
38684         }
38685         
38686     },
38687     
38688     initial : function()
38689     {
38690         this.fireEvent('initial', this);
38691         
38692     },
38693     
38694     onClick : function(e)
38695     {
38696         e.preventDefault();
38697         
38698         this.fireEvent('click', this);
38699     },
38700     
38701     onDownload : function(e)
38702     {
38703         e.preventDefault();
38704         
38705         this.fireEvent('download', this);
38706     },
38707     
38708     onTrash : function(e)
38709     {
38710         e.preventDefault();
38711         
38712         this.fireEvent('trash', this);
38713     }
38714     
38715 });
38716 /*
38717  * - LGPL
38718  *
38719  * FieldLabel
38720  * 
38721  */
38722
38723 /**
38724  * @class Roo.bootstrap.form.FieldLabel
38725  * @extends Roo.bootstrap.Component
38726  * Bootstrap FieldLabel class
38727  * @cfg {String} html contents of the element
38728  * @cfg {String} tag tag of the element default label
38729  * @cfg {String} cls class of the element
38730  * @cfg {String} target label target 
38731  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38732  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38733  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38734  * @cfg {String} iconTooltip default "This field is required"
38735  * @cfg {String} indicatorpos (left|right) default left
38736  * 
38737  * @constructor
38738  * Create a new FieldLabel
38739  * @param {Object} config The config object
38740  */
38741
38742 Roo.bootstrap.form.FieldLabel = function(config){
38743     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38744     
38745     this.addEvents({
38746             /**
38747              * @event invalid
38748              * Fires after the field has been marked as invalid.
38749              * @param {Roo.form.FieldLabel} this
38750              * @param {String} msg The validation message
38751              */
38752             invalid : true,
38753             /**
38754              * @event valid
38755              * Fires after the field has been validated with no errors.
38756              * @param {Roo.form.FieldLabel} this
38757              */
38758             valid : true
38759         });
38760 };
38761
38762 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38763     
38764     tag: 'label',
38765     cls: '',
38766     html: '',
38767     target: '',
38768     allowBlank : true,
38769     invalidClass : 'has-warning',
38770     validClass : 'has-success',
38771     iconTooltip : 'This field is required',
38772     indicatorpos : 'left',
38773     
38774     getAutoCreate : function(){
38775         
38776         var cls = "";
38777         if (!this.allowBlank) {
38778             cls  = "visible";
38779         }
38780         
38781         var cfg = {
38782             tag : this.tag,
38783             cls : 'roo-bootstrap-field-label ' + this.cls,
38784             for : this.target,
38785             cn : [
38786                 {
38787                     tag : 'i',
38788                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38789                     tooltip : this.iconTooltip
38790                 },
38791                 {
38792                     tag : 'span',
38793                     html : this.html
38794                 }
38795             ] 
38796         };
38797         
38798         if(this.indicatorpos == 'right'){
38799             var cfg = {
38800                 tag : this.tag,
38801                 cls : 'roo-bootstrap-field-label ' + this.cls,
38802                 for : this.target,
38803                 cn : [
38804                     {
38805                         tag : 'span',
38806                         html : this.html
38807                     },
38808                     {
38809                         tag : 'i',
38810                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38811                         tooltip : this.iconTooltip
38812                     }
38813                 ] 
38814             };
38815         }
38816         
38817         return cfg;
38818     },
38819     
38820     initEvents: function() 
38821     {
38822         Roo.bootstrap.Element.superclass.initEvents.call(this);
38823         
38824         this.indicator = this.indicatorEl();
38825         
38826         if(this.indicator){
38827             this.indicator.removeClass('visible');
38828             this.indicator.addClass('invisible');
38829         }
38830         
38831         Roo.bootstrap.form.FieldLabel.register(this);
38832     },
38833     
38834     indicatorEl : function()
38835     {
38836         var indicator = this.el.select('i.roo-required-indicator',true).first();
38837         
38838         if(!indicator){
38839             return false;
38840         }
38841         
38842         return indicator;
38843         
38844     },
38845     
38846     /**
38847      * Mark this field as valid
38848      */
38849     markValid : function()
38850     {
38851         if(this.indicator){
38852             this.indicator.removeClass('visible');
38853             this.indicator.addClass('invisible');
38854         }
38855         if (Roo.bootstrap.version == 3) {
38856             this.el.removeClass(this.invalidClass);
38857             this.el.addClass(this.validClass);
38858         } else {
38859             this.el.removeClass('is-invalid');
38860             this.el.addClass('is-valid');
38861         }
38862         
38863         
38864         this.fireEvent('valid', this);
38865     },
38866     
38867     /**
38868      * Mark this field as invalid
38869      * @param {String} msg The validation message
38870      */
38871     markInvalid : function(msg)
38872     {
38873         if(this.indicator){
38874             this.indicator.removeClass('invisible');
38875             this.indicator.addClass('visible');
38876         }
38877           if (Roo.bootstrap.version == 3) {
38878             this.el.removeClass(this.validClass);
38879             this.el.addClass(this.invalidClass);
38880         } else {
38881             this.el.removeClass('is-valid');
38882             this.el.addClass('is-invalid');
38883         }
38884         
38885         
38886         this.fireEvent('invalid', this, msg);
38887     }
38888     
38889    
38890 });
38891
38892 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38893     
38894     groups: {},
38895     
38896      /**
38897     * register a FieldLabel Group
38898     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38899     */
38900     register : function(label)
38901     {
38902         if(this.groups.hasOwnProperty(label.target)){
38903             return;
38904         }
38905      
38906         this.groups[label.target] = label;
38907         
38908     },
38909     /**
38910     * fetch a FieldLabel Group based on the target
38911     * @param {string} target
38912     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38913     */
38914     get: function(target) {
38915         if (typeof(this.groups[target]) == 'undefined') {
38916             return false;
38917         }
38918         
38919         return this.groups[target] ;
38920     }
38921 });
38922
38923  
38924
38925  /*
38926  * - LGPL
38927  *
38928  * page DateSplitField.
38929  * 
38930  */
38931
38932
38933 /**
38934  * @class Roo.bootstrap.form.DateSplitField
38935  * @extends Roo.bootstrap.Component
38936  * Bootstrap DateSplitField class
38937  * @cfg {string} fieldLabel - the label associated
38938  * @cfg {Number} labelWidth set the width of label (0-12)
38939  * @cfg {String} labelAlign (top|left)
38940  * @cfg {Boolean} dayAllowBlank (true|false) default false
38941  * @cfg {Boolean} monthAllowBlank (true|false) default false
38942  * @cfg {Boolean} yearAllowBlank (true|false) default false
38943  * @cfg {string} dayPlaceholder 
38944  * @cfg {string} monthPlaceholder
38945  * @cfg {string} yearPlaceholder
38946  * @cfg {string} dayFormat default 'd'
38947  * @cfg {string} monthFormat default 'm'
38948  * @cfg {string} yearFormat default 'Y'
38949  * @cfg {Number} labellg set the width of label (1-12)
38950  * @cfg {Number} labelmd set the width of label (1-12)
38951  * @cfg {Number} labelsm set the width of label (1-12)
38952  * @cfg {Number} labelxs set the width of label (1-12)
38953
38954  *     
38955  * @constructor
38956  * Create a new DateSplitField
38957  * @param {Object} config The config object
38958  */
38959
38960 Roo.bootstrap.form.DateSplitField = function(config){
38961     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38962     
38963     this.addEvents({
38964         // raw events
38965          /**
38966          * @event years
38967          * getting the data of years
38968          * @param {Roo.bootstrap.form.DateSplitField} this
38969          * @param {Object} years
38970          */
38971         "years" : true,
38972         /**
38973          * @event days
38974          * getting the data of days
38975          * @param {Roo.bootstrap.form.DateSplitField} this
38976          * @param {Object} days
38977          */
38978         "days" : true,
38979         /**
38980          * @event invalid
38981          * Fires after the field has been marked as invalid.
38982          * @param {Roo.form.Field} this
38983          * @param {String} msg The validation message
38984          */
38985         invalid : true,
38986        /**
38987          * @event valid
38988          * Fires after the field has been validated with no errors.
38989          * @param {Roo.form.Field} this
38990          */
38991         valid : true
38992     });
38993 };
38994
38995 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38996     
38997     fieldLabel : '',
38998     labelAlign : 'top',
38999     labelWidth : 3,
39000     dayAllowBlank : false,
39001     monthAllowBlank : false,
39002     yearAllowBlank : false,
39003     dayPlaceholder : '',
39004     monthPlaceholder : '',
39005     yearPlaceholder : '',
39006     dayFormat : 'd',
39007     monthFormat : 'm',
39008     yearFormat : 'Y',
39009     isFormField : true,
39010     labellg : 0,
39011     labelmd : 0,
39012     labelsm : 0,
39013     labelxs : 0,
39014     
39015     getAutoCreate : function()
39016     {
39017         var cfg = {
39018             tag : 'div',
39019             cls : 'row roo-date-split-field-group',
39020             cn : [
39021                 {
39022                     tag : 'input',
39023                     type : 'hidden',
39024                     cls : 'form-hidden-field roo-date-split-field-group-value',
39025                     name : this.name
39026                 }
39027             ]
39028         };
39029         
39030         var labelCls = 'col-md-12';
39031         var contentCls = 'col-md-4';
39032         
39033         if(this.fieldLabel){
39034             
39035             var label = {
39036                 tag : 'div',
39037                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
39038                 cn : [
39039                     {
39040                         tag : 'label',
39041                         html : this.fieldLabel
39042                     }
39043                 ]
39044             };
39045             
39046             if(this.labelAlign == 'left'){
39047             
39048                 if(this.labelWidth > 12){
39049                     label.style = "width: " + this.labelWidth + 'px';
39050                 }
39051
39052                 if(this.labelWidth < 13 && this.labelmd == 0){
39053                     this.labelmd = this.labelWidth;
39054                 }
39055
39056                 if(this.labellg > 0){
39057                     labelCls = ' col-lg-' + this.labellg;
39058                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
39059                 }
39060
39061                 if(this.labelmd > 0){
39062                     labelCls = ' col-md-' + this.labelmd;
39063                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
39064                 }
39065
39066                 if(this.labelsm > 0){
39067                     labelCls = ' col-sm-' + this.labelsm;
39068                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
39069                 }
39070
39071                 if(this.labelxs > 0){
39072                     labelCls = ' col-xs-' + this.labelxs;
39073                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
39074                 }
39075             }
39076             
39077             label.cls += ' ' + labelCls;
39078             
39079             cfg.cn.push(label);
39080         }
39081         
39082         Roo.each(['day', 'month', 'year'], function(t){
39083             cfg.cn.push({
39084                 tag : 'div',
39085                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
39086             });
39087         }, this);
39088         
39089         return cfg;
39090     },
39091     
39092     inputEl: function ()
39093     {
39094         return this.el.select('.roo-date-split-field-group-value', true).first();
39095     },
39096     
39097     onRender : function(ct, position) 
39098     {
39099         var _this = this;
39100         
39101         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
39102         
39103         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
39104         
39105         this.dayField = new Roo.bootstrap.form.ComboBox({
39106             allowBlank : this.dayAllowBlank,
39107             alwaysQuery : true,
39108             displayField : 'value',
39109             editable : false,
39110             fieldLabel : '',
39111             forceSelection : true,
39112             mode : 'local',
39113             placeholder : this.dayPlaceholder,
39114             selectOnFocus : true,
39115             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
39116             triggerAction : 'all',
39117             typeAhead : true,
39118             valueField : 'value',
39119             store : new Roo.data.SimpleStore({
39120                 data : (function() {    
39121                     var days = [];
39122                     _this.fireEvent('days', _this, days);
39123                     return days;
39124                 })(),
39125                 fields : [ 'value' ]
39126             }),
39127             listeners : {
39128                 select : function (_self, record, index)
39129                 {
39130                     _this.setValue(_this.getValue());
39131                 }
39132             }
39133         });
39134
39135         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
39136         
39137         this.monthField = new Roo.bootstrap.form.MonthField({
39138             after : '<i class=\"fa fa-calendar\"></i>',
39139             allowBlank : this.monthAllowBlank,
39140             placeholder : this.monthPlaceholder,
39141             readOnly : true,
39142             listeners : {
39143                 render : function (_self)
39144                 {
39145                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
39146                         e.preventDefault();
39147                         _self.focus();
39148                     });
39149                 },
39150                 select : function (_self, oldvalue, newvalue)
39151                 {
39152                     _this.setValue(_this.getValue());
39153                 }
39154             }
39155         });
39156         
39157         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
39158         
39159         this.yearField = new Roo.bootstrap.form.ComboBox({
39160             allowBlank : this.yearAllowBlank,
39161             alwaysQuery : true,
39162             displayField : 'value',
39163             editable : false,
39164             fieldLabel : '',
39165             forceSelection : true,
39166             mode : 'local',
39167             placeholder : this.yearPlaceholder,
39168             selectOnFocus : true,
39169             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
39170             triggerAction : 'all',
39171             typeAhead : true,
39172             valueField : 'value',
39173             store : new Roo.data.SimpleStore({
39174                 data : (function() {
39175                     var years = [];
39176                     _this.fireEvent('years', _this, years);
39177                     return years;
39178                 })(),
39179                 fields : [ 'value' ]
39180             }),
39181             listeners : {
39182                 select : function (_self, record, index)
39183                 {
39184                     _this.setValue(_this.getValue());
39185                 }
39186             }
39187         });
39188
39189         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
39190     },
39191     
39192     setValue : function(v, format)
39193     {
39194         this.inputEl.dom.value = v;
39195         
39196         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
39197         
39198         var d = Date.parseDate(v, f);
39199         
39200         if(!d){
39201             this.validate();
39202             return;
39203         }
39204         
39205         this.setDay(d.format(this.dayFormat));
39206         this.setMonth(d.format(this.monthFormat));
39207         this.setYear(d.format(this.yearFormat));
39208         
39209         this.validate();
39210         
39211         return;
39212     },
39213     
39214     setDay : function(v)
39215     {
39216         this.dayField.setValue(v);
39217         this.inputEl.dom.value = this.getValue();
39218         this.validate();
39219         return;
39220     },
39221     
39222     setMonth : function(v)
39223     {
39224         this.monthField.setValue(v, true);
39225         this.inputEl.dom.value = this.getValue();
39226         this.validate();
39227         return;
39228     },
39229     
39230     setYear : function(v)
39231     {
39232         this.yearField.setValue(v);
39233         this.inputEl.dom.value = this.getValue();
39234         this.validate();
39235         return;
39236     },
39237     
39238     getDay : function()
39239     {
39240         return this.dayField.getValue();
39241     },
39242     
39243     getMonth : function()
39244     {
39245         return this.monthField.getValue();
39246     },
39247     
39248     getYear : function()
39249     {
39250         return this.yearField.getValue();
39251     },
39252     
39253     getValue : function()
39254     {
39255         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
39256         
39257         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
39258         
39259         return date;
39260     },
39261     
39262     reset : function()
39263     {
39264         this.setDay('');
39265         this.setMonth('');
39266         this.setYear('');
39267         this.inputEl.dom.value = '';
39268         this.validate();
39269         return;
39270     },
39271     
39272     validate : function()
39273     {
39274         var d = this.dayField.validate();
39275         var m = this.monthField.validate();
39276         var y = this.yearField.validate();
39277         
39278         var valid = true;
39279         
39280         if(
39281                 (!this.dayAllowBlank && !d) ||
39282                 (!this.monthAllowBlank && !m) ||
39283                 (!this.yearAllowBlank && !y)
39284         ){
39285             valid = false;
39286         }
39287         
39288         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
39289             return valid;
39290         }
39291         
39292         if(valid){
39293             this.markValid();
39294             return valid;
39295         }
39296         
39297         this.markInvalid();
39298         
39299         return valid;
39300     },
39301     
39302     markValid : function()
39303     {
39304         
39305         var label = this.el.select('label', true).first();
39306         var icon = this.el.select('i.fa-star', true).first();
39307
39308         if(label && icon){
39309             icon.remove();
39310         }
39311         
39312         this.fireEvent('valid', this);
39313     },
39314     
39315      /**
39316      * Mark this field as invalid
39317      * @param {String} msg The validation message
39318      */
39319     markInvalid : function(msg)
39320     {
39321         
39322         var label = this.el.select('label', true).first();
39323         var icon = this.el.select('i.fa-star', true).first();
39324
39325         if(label && !icon){
39326             this.el.select('.roo-date-split-field-label', true).createChild({
39327                 tag : 'i',
39328                 cls : 'text-danger fa fa-lg fa-star',
39329                 tooltip : 'This field is required',
39330                 style : 'margin-right:5px;'
39331             }, label, true);
39332         }
39333         
39334         this.fireEvent('invalid', this, msg);
39335     },
39336     
39337     clearInvalid : function()
39338     {
39339         var label = this.el.select('label', true).first();
39340         var icon = this.el.select('i.fa-star', true).first();
39341
39342         if(label && icon){
39343             icon.remove();
39344         }
39345         
39346         this.fireEvent('valid', this);
39347     },
39348     
39349     getName: function()
39350     {
39351         return this.name;
39352     }
39353     
39354 });
39355
39356  
39357
39358 /**
39359  * @class Roo.bootstrap.LayoutMasonry
39360  * @extends Roo.bootstrap.Component
39361  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39362  * Bootstrap Layout Masonry class
39363  *
39364  * This is based on 
39365  * http://masonry.desandro.com
39366  *
39367  * The idea is to render all the bricks based on vertical width...
39368  *
39369  * The original code extends 'outlayer' - we might need to use that....
39370
39371  * @constructor
39372  * Create a new Element
39373  * @param {Object} config The config object
39374  */
39375
39376 Roo.bootstrap.LayoutMasonry = function(config){
39377     
39378     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39379     
39380     this.bricks = [];
39381     
39382     Roo.bootstrap.LayoutMasonry.register(this);
39383     
39384     this.addEvents({
39385         // raw events
39386         /**
39387          * @event layout
39388          * Fire after layout the items
39389          * @param {Roo.bootstrap.LayoutMasonry} this
39390          * @param {Roo.EventObject} e
39391          */
39392         "layout" : true
39393     });
39394     
39395 };
39396
39397 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
39398     
39399     /**
39400      * @cfg {Boolean} isLayoutInstant = no animation?
39401      */   
39402     isLayoutInstant : false, // needed?
39403    
39404     /**
39405      * @cfg {Number} boxWidth  width of the columns
39406      */   
39407     boxWidth : 450,
39408     
39409       /**
39410      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
39411      */   
39412     boxHeight : 0,
39413     
39414     /**
39415      * @cfg {Number} padWidth padding below box..
39416      */   
39417     padWidth : 10, 
39418     
39419     /**
39420      * @cfg {Number} gutter gutter width..
39421      */   
39422     gutter : 10,
39423     
39424      /**
39425      * @cfg {Number} maxCols maximum number of columns
39426      */   
39427     
39428     maxCols: 0,
39429     
39430     /**
39431      * @cfg {Boolean} isAutoInitial defalut true
39432      */   
39433     isAutoInitial : true, 
39434     
39435     containerWidth: 0,
39436     
39437     /**
39438      * @cfg {Boolean} isHorizontal defalut false
39439      */   
39440     isHorizontal : false, 
39441
39442     currentSize : null,
39443     
39444     tag: 'div',
39445     
39446     cls: '',
39447     
39448     bricks: null, //CompositeElement
39449     
39450     cols : 1,
39451     
39452     _isLayoutInited : false,
39453     
39454 //    isAlternative : false, // only use for vertical layout...
39455     
39456     /**
39457      * @cfg {Number} alternativePadWidth padding below box..
39458      */   
39459     alternativePadWidth : 50,
39460     
39461     selectedBrick : [],
39462     
39463     getAutoCreate : function(){
39464         
39465         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39466         
39467         var cfg = {
39468             tag: this.tag,
39469             cls: 'blog-masonary-wrapper ' + this.cls,
39470             cn : {
39471                 cls : 'mas-boxes masonary'
39472             }
39473         };
39474         
39475         return cfg;
39476     },
39477     
39478     getChildContainer: function( )
39479     {
39480         if (this.boxesEl) {
39481             return this.boxesEl;
39482         }
39483         
39484         this.boxesEl = this.el.select('.mas-boxes').first();
39485         
39486         return this.boxesEl;
39487     },
39488     
39489     
39490     initEvents : function()
39491     {
39492         var _this = this;
39493         
39494         if(this.isAutoInitial){
39495             Roo.log('hook children rendered');
39496             this.on('childrenrendered', function() {
39497                 Roo.log('children rendered');
39498                 _this.initial();
39499             } ,this);
39500         }
39501     },
39502     
39503     initial : function()
39504     {
39505         this.selectedBrick = [];
39506         
39507         this.currentSize = this.el.getBox(true);
39508         
39509         Roo.EventManager.onWindowResize(this.resize, this); 
39510
39511         if(!this.isAutoInitial){
39512             this.layout();
39513             return;
39514         }
39515         
39516         this.layout();
39517         
39518         return;
39519         //this.layout.defer(500,this);
39520         
39521     },
39522     
39523     resize : function()
39524     {
39525         var cs = this.el.getBox(true);
39526         
39527         if (
39528                 this.currentSize.width == cs.width && 
39529                 this.currentSize.x == cs.x && 
39530                 this.currentSize.height == cs.height && 
39531                 this.currentSize.y == cs.y 
39532         ) {
39533             Roo.log("no change in with or X or Y");
39534             return;
39535         }
39536         
39537         this.currentSize = cs;
39538         
39539         this.layout();
39540         
39541     },
39542     
39543     layout : function()
39544     {   
39545         this._resetLayout();
39546         
39547         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39548         
39549         this.layoutItems( isInstant );
39550       
39551         this._isLayoutInited = true;
39552         
39553         this.fireEvent('layout', this);
39554         
39555     },
39556     
39557     _resetLayout : function()
39558     {
39559         if(this.isHorizontal){
39560             this.horizontalMeasureColumns();
39561             return;
39562         }
39563         
39564         this.verticalMeasureColumns();
39565         
39566     },
39567     
39568     verticalMeasureColumns : function()
39569     {
39570         this.getContainerWidth();
39571         
39572 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39573 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
39574 //            return;
39575 //        }
39576         
39577         var boxWidth = this.boxWidth + this.padWidth;
39578         
39579         if(this.containerWidth < this.boxWidth){
39580             boxWidth = this.containerWidth
39581         }
39582         
39583         var containerWidth = this.containerWidth;
39584         
39585         var cols = Math.floor(containerWidth / boxWidth);
39586         
39587         this.cols = Math.max( cols, 1 );
39588         
39589         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39590         
39591         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39592         
39593         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39594         
39595         this.colWidth = boxWidth + avail - this.padWidth;
39596         
39597         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39598         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
39599     },
39600     
39601     horizontalMeasureColumns : function()
39602     {
39603         this.getContainerWidth();
39604         
39605         var boxWidth = this.boxWidth;
39606         
39607         if(this.containerWidth < boxWidth){
39608             boxWidth = this.containerWidth;
39609         }
39610         
39611         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39612         
39613         this.el.setHeight(boxWidth);
39614         
39615     },
39616     
39617     getContainerWidth : function()
39618     {
39619         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39620     },
39621     
39622     layoutItems : function( isInstant )
39623     {
39624         Roo.log(this.bricks);
39625         
39626         var items = Roo.apply([], this.bricks);
39627         
39628         if(this.isHorizontal){
39629             this._horizontalLayoutItems( items , isInstant );
39630             return;
39631         }
39632         
39633 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39634 //            this._verticalAlternativeLayoutItems( items , isInstant );
39635 //            return;
39636 //        }
39637         
39638         this._verticalLayoutItems( items , isInstant );
39639         
39640     },
39641     
39642     _verticalLayoutItems : function ( items , isInstant)
39643     {
39644         if ( !items || !items.length ) {
39645             return;
39646         }
39647         
39648         var standard = [
39649             ['xs', 'xs', 'xs', 'tall'],
39650             ['xs', 'xs', 'tall'],
39651             ['xs', 'xs', 'sm'],
39652             ['xs', 'xs', 'xs'],
39653             ['xs', 'tall'],
39654             ['xs', 'sm'],
39655             ['xs', 'xs'],
39656             ['xs'],
39657             
39658             ['sm', 'xs', 'xs'],
39659             ['sm', 'xs'],
39660             ['sm'],
39661             
39662             ['tall', 'xs', 'xs', 'xs'],
39663             ['tall', 'xs', 'xs'],
39664             ['tall', 'xs'],
39665             ['tall']
39666             
39667         ];
39668         
39669         var queue = [];
39670         
39671         var boxes = [];
39672         
39673         var box = [];
39674         
39675         Roo.each(items, function(item, k){
39676             
39677             switch (item.size) {
39678                 // these layouts take up a full box,
39679                 case 'md' :
39680                 case 'md-left' :
39681                 case 'md-right' :
39682                 case 'wide' :
39683                     
39684                     if(box.length){
39685                         boxes.push(box);
39686                         box = [];
39687                     }
39688                     
39689                     boxes.push([item]);
39690                     
39691                     break;
39692                     
39693                 case 'xs' :
39694                 case 'sm' :
39695                 case 'tall' :
39696                     
39697                     box.push(item);
39698                     
39699                     break;
39700                 default :
39701                     break;
39702                     
39703             }
39704             
39705         }, this);
39706         
39707         if(box.length){
39708             boxes.push(box);
39709             box = [];
39710         }
39711         
39712         var filterPattern = function(box, length)
39713         {
39714             if(!box.length){
39715                 return;
39716             }
39717             
39718             var match = false;
39719             
39720             var pattern = box.slice(0, length);
39721             
39722             var format = [];
39723             
39724             Roo.each(pattern, function(i){
39725                 format.push(i.size);
39726             }, this);
39727             
39728             Roo.each(standard, function(s){
39729                 
39730                 if(String(s) != String(format)){
39731                     return;
39732                 }
39733                 
39734                 match = true;
39735                 return false;
39736                 
39737             }, this);
39738             
39739             if(!match && length == 1){
39740                 return;
39741             }
39742             
39743             if(!match){
39744                 filterPattern(box, length - 1);
39745                 return;
39746             }
39747                 
39748             queue.push(pattern);
39749
39750             box = box.slice(length, box.length);
39751
39752             filterPattern(box, 4);
39753
39754             return;
39755             
39756         }
39757         
39758         Roo.each(boxes, function(box, k){
39759             
39760             if(!box.length){
39761                 return;
39762             }
39763             
39764             if(box.length == 1){
39765                 queue.push(box);
39766                 return;
39767             }
39768             
39769             filterPattern(box, 4);
39770             
39771         }, this);
39772         
39773         this._processVerticalLayoutQueue( queue, isInstant );
39774         
39775     },
39776     
39777 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39778 //    {
39779 //        if ( !items || !items.length ) {
39780 //            return;
39781 //        }
39782 //
39783 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39784 //        
39785 //    },
39786     
39787     _horizontalLayoutItems : function ( items , isInstant)
39788     {
39789         if ( !items || !items.length || items.length < 3) {
39790             return;
39791         }
39792         
39793         items.reverse();
39794         
39795         var eItems = items.slice(0, 3);
39796         
39797         items = items.slice(3, items.length);
39798         
39799         var standard = [
39800             ['xs', 'xs', 'xs', 'wide'],
39801             ['xs', 'xs', 'wide'],
39802             ['xs', 'xs', 'sm'],
39803             ['xs', 'xs', 'xs'],
39804             ['xs', 'wide'],
39805             ['xs', 'sm'],
39806             ['xs', 'xs'],
39807             ['xs'],
39808             
39809             ['sm', 'xs', 'xs'],
39810             ['sm', 'xs'],
39811             ['sm'],
39812             
39813             ['wide', 'xs', 'xs', 'xs'],
39814             ['wide', 'xs', 'xs'],
39815             ['wide', 'xs'],
39816             ['wide'],
39817             
39818             ['wide-thin']
39819         ];
39820         
39821         var queue = [];
39822         
39823         var boxes = [];
39824         
39825         var box = [];
39826         
39827         Roo.each(items, function(item, k){
39828             
39829             switch (item.size) {
39830                 case 'md' :
39831                 case 'md-left' :
39832                 case 'md-right' :
39833                 case 'tall' :
39834                     
39835                     if(box.length){
39836                         boxes.push(box);
39837                         box = [];
39838                     }
39839                     
39840                     boxes.push([item]);
39841                     
39842                     break;
39843                     
39844                 case 'xs' :
39845                 case 'sm' :
39846                 case 'wide' :
39847                 case 'wide-thin' :
39848                     
39849                     box.push(item);
39850                     
39851                     break;
39852                 default :
39853                     break;
39854                     
39855             }
39856             
39857         }, this);
39858         
39859         if(box.length){
39860             boxes.push(box);
39861             box = [];
39862         }
39863         
39864         var filterPattern = function(box, length)
39865         {
39866             if(!box.length){
39867                 return;
39868             }
39869             
39870             var match = false;
39871             
39872             var pattern = box.slice(0, length);
39873             
39874             var format = [];
39875             
39876             Roo.each(pattern, function(i){
39877                 format.push(i.size);
39878             }, this);
39879             
39880             Roo.each(standard, function(s){
39881                 
39882                 if(String(s) != String(format)){
39883                     return;
39884                 }
39885                 
39886                 match = true;
39887                 return false;
39888                 
39889             }, this);
39890             
39891             if(!match && length == 1){
39892                 return;
39893             }
39894             
39895             if(!match){
39896                 filterPattern(box, length - 1);
39897                 return;
39898             }
39899                 
39900             queue.push(pattern);
39901
39902             box = box.slice(length, box.length);
39903
39904             filterPattern(box, 4);
39905
39906             return;
39907             
39908         }
39909         
39910         Roo.each(boxes, function(box, k){
39911             
39912             if(!box.length){
39913                 return;
39914             }
39915             
39916             if(box.length == 1){
39917                 queue.push(box);
39918                 return;
39919             }
39920             
39921             filterPattern(box, 4);
39922             
39923         }, this);
39924         
39925         
39926         var prune = [];
39927         
39928         var pos = this.el.getBox(true);
39929         
39930         var minX = pos.x;
39931         
39932         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39933         
39934         var hit_end = false;
39935         
39936         Roo.each(queue, function(box){
39937             
39938             if(hit_end){
39939                 
39940                 Roo.each(box, function(b){
39941                 
39942                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39943                     b.el.hide();
39944
39945                 }, this);
39946
39947                 return;
39948             }
39949             
39950             var mx = 0;
39951             
39952             Roo.each(box, function(b){
39953                 
39954                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39955                 b.el.show();
39956
39957                 mx = Math.max(mx, b.x);
39958                 
39959             }, this);
39960             
39961             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39962             
39963             if(maxX < minX){
39964                 
39965                 Roo.each(box, function(b){
39966                 
39967                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39968                     b.el.hide();
39969                     
39970                 }, this);
39971                 
39972                 hit_end = true;
39973                 
39974                 return;
39975             }
39976             
39977             prune.push(box);
39978             
39979         }, this);
39980         
39981         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39982     },
39983     
39984     /** Sets position of item in DOM
39985     * @param {Element} item
39986     * @param {Number} x - horizontal position
39987     * @param {Number} y - vertical position
39988     * @param {Boolean} isInstant - disables transitions
39989     */
39990     _processVerticalLayoutQueue : function( queue, isInstant )
39991     {
39992         var pos = this.el.getBox(true);
39993         var x = pos.x;
39994         var y = pos.y;
39995         var maxY = [];
39996         
39997         for (var i = 0; i < this.cols; i++){
39998             maxY[i] = pos.y;
39999         }
40000         
40001         Roo.each(queue, function(box, k){
40002             
40003             var col = k % this.cols;
40004             
40005             Roo.each(box, function(b,kk){
40006                 
40007                 b.el.position('absolute');
40008                 
40009                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
40010                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
40011                 
40012                 if(b.size == 'md-left' || b.size == 'md-right'){
40013                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
40014                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
40015                 }
40016                 
40017                 b.el.setWidth(width);
40018                 b.el.setHeight(height);
40019                 // iframe?
40020                 b.el.select('iframe',true).setSize(width,height);
40021                 
40022             }, this);
40023             
40024             for (var i = 0; i < this.cols; i++){
40025                 
40026                 if(maxY[i] < maxY[col]){
40027                     col = i;
40028                     continue;
40029                 }
40030                 
40031                 col = Math.min(col, i);
40032                 
40033             }
40034             
40035             x = pos.x + col * (this.colWidth + this.padWidth);
40036             
40037             y = maxY[col];
40038             
40039             var positions = [];
40040             
40041             switch (box.length){
40042                 case 1 :
40043                     positions = this.getVerticalOneBoxColPositions(x, y, box);
40044                     break;
40045                 case 2 :
40046                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
40047                     break;
40048                 case 3 :
40049                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
40050                     break;
40051                 case 4 :
40052                     positions = this.getVerticalFourBoxColPositions(x, y, box);
40053                     break;
40054                 default :
40055                     break;
40056             }
40057             
40058             Roo.each(box, function(b,kk){
40059                 
40060                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
40061                 
40062                 var sz = b.el.getSize();
40063                 
40064                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
40065                 
40066             }, this);
40067             
40068         }, this);
40069         
40070         var mY = 0;
40071         
40072         for (var i = 0; i < this.cols; i++){
40073             mY = Math.max(mY, maxY[i]);
40074         }
40075         
40076         this.el.setHeight(mY - pos.y);
40077         
40078     },
40079     
40080 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
40081 //    {
40082 //        var pos = this.el.getBox(true);
40083 //        var x = pos.x;
40084 //        var y = pos.y;
40085 //        var maxX = pos.right;
40086 //        
40087 //        var maxHeight = 0;
40088 //        
40089 //        Roo.each(items, function(item, k){
40090 //            
40091 //            var c = k % 2;
40092 //            
40093 //            item.el.position('absolute');
40094 //                
40095 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
40096 //
40097 //            item.el.setWidth(width);
40098 //
40099 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
40100 //
40101 //            item.el.setHeight(height);
40102 //            
40103 //            if(c == 0){
40104 //                item.el.setXY([x, y], isInstant ? false : true);
40105 //            } else {
40106 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
40107 //            }
40108 //            
40109 //            y = y + height + this.alternativePadWidth;
40110 //            
40111 //            maxHeight = maxHeight + height + this.alternativePadWidth;
40112 //            
40113 //        }, this);
40114 //        
40115 //        this.el.setHeight(maxHeight);
40116 //        
40117 //    },
40118     
40119     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
40120     {
40121         var pos = this.el.getBox(true);
40122         
40123         var minX = pos.x;
40124         var minY = pos.y;
40125         
40126         var maxX = pos.right;
40127         
40128         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
40129         
40130         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
40131         
40132         Roo.each(queue, function(box, k){
40133             
40134             Roo.each(box, function(b, kk){
40135                 
40136                 b.el.position('absolute');
40137                 
40138                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
40139                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
40140                 
40141                 if(b.size == 'md-left' || b.size == 'md-right'){
40142                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
40143                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
40144                 }
40145                 
40146                 b.el.setWidth(width);
40147                 b.el.setHeight(height);
40148                 
40149             }, this);
40150             
40151             if(!box.length){
40152                 return;
40153             }
40154             
40155             var positions = [];
40156             
40157             switch (box.length){
40158                 case 1 :
40159                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
40160                     break;
40161                 case 2 :
40162                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
40163                     break;
40164                 case 3 :
40165                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
40166                     break;
40167                 case 4 :
40168                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
40169                     break;
40170                 default :
40171                     break;
40172             }
40173             
40174             Roo.each(box, function(b,kk){
40175                 
40176                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
40177                 
40178                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
40179                 
40180             }, this);
40181             
40182         }, this);
40183         
40184     },
40185     
40186     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
40187     {
40188         Roo.each(eItems, function(b,k){
40189             
40190             b.size = (k == 0) ? 'sm' : 'xs';
40191             b.x = (k == 0) ? 2 : 1;
40192             b.y = (k == 0) ? 2 : 1;
40193             
40194             b.el.position('absolute');
40195             
40196             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
40197                 
40198             b.el.setWidth(width);
40199             
40200             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
40201             
40202             b.el.setHeight(height);
40203             
40204         }, this);
40205
40206         var positions = [];
40207         
40208         positions.push({
40209             x : maxX - this.unitWidth * 2 - this.gutter,
40210             y : minY
40211         });
40212         
40213         positions.push({
40214             x : maxX - this.unitWidth,
40215             y : minY + (this.unitWidth + this.gutter) * 2
40216         });
40217         
40218         positions.push({
40219             x : maxX - this.unitWidth * 3 - this.gutter * 2,
40220             y : minY
40221         });
40222         
40223         Roo.each(eItems, function(b,k){
40224             
40225             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
40226
40227         }, this);
40228         
40229     },
40230     
40231     getVerticalOneBoxColPositions : function(x, y, box)
40232     {
40233         var pos = [];
40234         
40235         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
40236         
40237         if(box[0].size == 'md-left'){
40238             rand = 0;
40239         }
40240         
40241         if(box[0].size == 'md-right'){
40242             rand = 1;
40243         }
40244         
40245         pos.push({
40246             x : x + (this.unitWidth + this.gutter) * rand,
40247             y : y
40248         });
40249         
40250         return pos;
40251     },
40252     
40253     getVerticalTwoBoxColPositions : function(x, y, box)
40254     {
40255         var pos = [];
40256         
40257         if(box[0].size == 'xs'){
40258             
40259             pos.push({
40260                 x : x,
40261                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
40262             });
40263
40264             pos.push({
40265                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
40266                 y : y
40267             });
40268             
40269             return pos;
40270             
40271         }
40272         
40273         pos.push({
40274             x : x,
40275             y : y
40276         });
40277
40278         pos.push({
40279             x : x + (this.unitWidth + this.gutter) * 2,
40280             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
40281         });
40282         
40283         return pos;
40284         
40285     },
40286     
40287     getVerticalThreeBoxColPositions : function(x, y, box)
40288     {
40289         var pos = [];
40290         
40291         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40292             
40293             pos.push({
40294                 x : x,
40295                 y : y
40296             });
40297
40298             pos.push({
40299                 x : x + (this.unitWidth + this.gutter) * 1,
40300                 y : y
40301             });
40302             
40303             pos.push({
40304                 x : x + (this.unitWidth + this.gutter) * 2,
40305                 y : y
40306             });
40307             
40308             return pos;
40309             
40310         }
40311         
40312         if(box[0].size == 'xs' && box[1].size == 'xs'){
40313             
40314             pos.push({
40315                 x : x,
40316                 y : y
40317             });
40318
40319             pos.push({
40320                 x : x,
40321                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
40322             });
40323             
40324             pos.push({
40325                 x : x + (this.unitWidth + this.gutter) * 1,
40326                 y : y
40327             });
40328             
40329             return pos;
40330             
40331         }
40332         
40333         pos.push({
40334             x : x,
40335             y : y
40336         });
40337
40338         pos.push({
40339             x : x + (this.unitWidth + this.gutter) * 2,
40340             y : y
40341         });
40342
40343         pos.push({
40344             x : x + (this.unitWidth + this.gutter) * 2,
40345             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40346         });
40347             
40348         return pos;
40349         
40350     },
40351     
40352     getVerticalFourBoxColPositions : function(x, y, box)
40353     {
40354         var pos = [];
40355         
40356         if(box[0].size == 'xs'){
40357             
40358             pos.push({
40359                 x : x,
40360                 y : y
40361             });
40362
40363             pos.push({
40364                 x : x,
40365                 y : y + (this.unitHeight + this.gutter) * 1
40366             });
40367             
40368             pos.push({
40369                 x : x,
40370                 y : y + (this.unitHeight + this.gutter) * 2
40371             });
40372             
40373             pos.push({
40374                 x : x + (this.unitWidth + this.gutter) * 1,
40375                 y : y
40376             });
40377             
40378             return pos;
40379             
40380         }
40381         
40382         pos.push({
40383             x : x,
40384             y : y
40385         });
40386
40387         pos.push({
40388             x : x + (this.unitWidth + this.gutter) * 2,
40389             y : y
40390         });
40391
40392         pos.push({
40393             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40394             y : y + (this.unitHeight + this.gutter) * 1
40395         });
40396
40397         pos.push({
40398             x : x + (this.unitWidth + this.gutter) * 2,
40399             y : y + (this.unitWidth + this.gutter) * 2
40400         });
40401
40402         return pos;
40403         
40404     },
40405     
40406     getHorizontalOneBoxColPositions : function(maxX, minY, box)
40407     {
40408         var pos = [];
40409         
40410         if(box[0].size == 'md-left'){
40411             pos.push({
40412                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40413                 y : minY
40414             });
40415             
40416             return pos;
40417         }
40418         
40419         if(box[0].size == 'md-right'){
40420             pos.push({
40421                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40422                 y : minY + (this.unitWidth + this.gutter) * 1
40423             });
40424             
40425             return pos;
40426         }
40427         
40428         var rand = Math.floor(Math.random() * (4 - box[0].y));
40429         
40430         pos.push({
40431             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40432             y : minY + (this.unitWidth + this.gutter) * rand
40433         });
40434         
40435         return pos;
40436         
40437     },
40438     
40439     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40440     {
40441         var pos = [];
40442         
40443         if(box[0].size == 'xs'){
40444             
40445             pos.push({
40446                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40447                 y : minY
40448             });
40449
40450             pos.push({
40451                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40452                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40453             });
40454             
40455             return pos;
40456             
40457         }
40458         
40459         pos.push({
40460             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40461             y : minY
40462         });
40463
40464         pos.push({
40465             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40466             y : minY + (this.unitWidth + this.gutter) * 2
40467         });
40468         
40469         return pos;
40470         
40471     },
40472     
40473     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40474     {
40475         var pos = [];
40476         
40477         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40478             
40479             pos.push({
40480                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40481                 y : minY
40482             });
40483
40484             pos.push({
40485                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40486                 y : minY + (this.unitWidth + this.gutter) * 1
40487             });
40488             
40489             pos.push({
40490                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40491                 y : minY + (this.unitWidth + this.gutter) * 2
40492             });
40493             
40494             return pos;
40495             
40496         }
40497         
40498         if(box[0].size == 'xs' && box[1].size == 'xs'){
40499             
40500             pos.push({
40501                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40502                 y : minY
40503             });
40504
40505             pos.push({
40506                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40507                 y : minY
40508             });
40509             
40510             pos.push({
40511                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40512                 y : minY + (this.unitWidth + this.gutter) * 1
40513             });
40514             
40515             return pos;
40516             
40517         }
40518         
40519         pos.push({
40520             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40521             y : minY
40522         });
40523
40524         pos.push({
40525             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40526             y : minY + (this.unitWidth + this.gutter) * 2
40527         });
40528
40529         pos.push({
40530             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40531             y : minY + (this.unitWidth + this.gutter) * 2
40532         });
40533             
40534         return pos;
40535         
40536     },
40537     
40538     getHorizontalFourBoxColPositions : function(maxX, minY, box)
40539     {
40540         var pos = [];
40541         
40542         if(box[0].size == 'xs'){
40543             
40544             pos.push({
40545                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40546                 y : minY
40547             });
40548
40549             pos.push({
40550                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40551                 y : minY
40552             });
40553             
40554             pos.push({
40555                 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),
40556                 y : minY
40557             });
40558             
40559             pos.push({
40560                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40561                 y : minY + (this.unitWidth + this.gutter) * 1
40562             });
40563             
40564             return pos;
40565             
40566         }
40567         
40568         pos.push({
40569             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40570             y : minY
40571         });
40572         
40573         pos.push({
40574             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40575             y : minY + (this.unitWidth + this.gutter) * 2
40576         });
40577         
40578         pos.push({
40579             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40580             y : minY + (this.unitWidth + this.gutter) * 2
40581         });
40582         
40583         pos.push({
40584             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),
40585             y : minY + (this.unitWidth + this.gutter) * 2
40586         });
40587
40588         return pos;
40589         
40590     },
40591     
40592     /**
40593     * remove a Masonry Brick
40594     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40595     */
40596     removeBrick : function(brick_id)
40597     {
40598         if (!brick_id) {
40599             return;
40600         }
40601         
40602         for (var i = 0; i<this.bricks.length; i++) {
40603             if (this.bricks[i].id == brick_id) {
40604                 this.bricks.splice(i,1);
40605                 this.el.dom.removeChild(Roo.get(brick_id).dom);
40606                 this.initial();
40607             }
40608         }
40609     },
40610     
40611     /**
40612     * adds a Masonry Brick
40613     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40614     */
40615     addBrick : function(cfg)
40616     {
40617         var cn = new Roo.bootstrap.MasonryBrick(cfg);
40618         //this.register(cn);
40619         cn.parentId = this.id;
40620         cn.render(this.el);
40621         return cn;
40622     },
40623     
40624     /**
40625     * register a Masonry Brick
40626     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40627     */
40628     
40629     register : function(brick)
40630     {
40631         this.bricks.push(brick);
40632         brick.masonryId = this.id;
40633     },
40634     
40635     /**
40636     * clear all the Masonry Brick
40637     */
40638     clearAll : function()
40639     {
40640         this.bricks = [];
40641         //this.getChildContainer().dom.innerHTML = "";
40642         this.el.dom.innerHTML = '';
40643     },
40644     
40645     getSelected : function()
40646     {
40647         if (!this.selectedBrick) {
40648             return false;
40649         }
40650         
40651         return this.selectedBrick;
40652     }
40653 });
40654
40655 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40656     
40657     groups: {},
40658      /**
40659     * register a Masonry Layout
40660     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40661     */
40662     
40663     register : function(layout)
40664     {
40665         this.groups[layout.id] = layout;
40666     },
40667     /**
40668     * fetch a  Masonry Layout based on the masonry layout ID
40669     * @param {string} the masonry layout to add
40670     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40671     */
40672     
40673     get: function(layout_id) {
40674         if (typeof(this.groups[layout_id]) == 'undefined') {
40675             return false;
40676         }
40677         return this.groups[layout_id] ;
40678     }
40679     
40680     
40681     
40682 });
40683
40684  
40685
40686  /**
40687  *
40688  * This is based on 
40689  * http://masonry.desandro.com
40690  *
40691  * The idea is to render all the bricks based on vertical width...
40692  *
40693  * The original code extends 'outlayer' - we might need to use that....
40694  * 
40695  */
40696
40697
40698 /**
40699  * @class Roo.bootstrap.LayoutMasonryAuto
40700  * @extends Roo.bootstrap.Component
40701  * Bootstrap Layout Masonry class
40702  * 
40703  * @constructor
40704  * Create a new Element
40705  * @param {Object} config The config object
40706  */
40707
40708 Roo.bootstrap.LayoutMasonryAuto = function(config){
40709     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40710 };
40711
40712 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40713     
40714       /**
40715      * @cfg {Boolean} isFitWidth  - resize the width..
40716      */   
40717     isFitWidth : false,  // options..
40718     /**
40719      * @cfg {Boolean} isOriginLeft = left align?
40720      */   
40721     isOriginLeft : true,
40722     /**
40723      * @cfg {Boolean} isOriginTop = top align?
40724      */   
40725     isOriginTop : false,
40726     /**
40727      * @cfg {Boolean} isLayoutInstant = no animation?
40728      */   
40729     isLayoutInstant : false, // needed?
40730     /**
40731      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40732      */   
40733     isResizingContainer : true,
40734     /**
40735      * @cfg {Number} columnWidth  width of the columns 
40736      */   
40737     
40738     columnWidth : 0,
40739     
40740     /**
40741      * @cfg {Number} maxCols maximum number of columns
40742      */   
40743     
40744     maxCols: 0,
40745     /**
40746      * @cfg {Number} padHeight padding below box..
40747      */   
40748     
40749     padHeight : 10, 
40750     
40751     /**
40752      * @cfg {Boolean} isAutoInitial defalut true
40753      */   
40754     
40755     isAutoInitial : true, 
40756     
40757     // private?
40758     gutter : 0,
40759     
40760     containerWidth: 0,
40761     initialColumnWidth : 0,
40762     currentSize : null,
40763     
40764     colYs : null, // array.
40765     maxY : 0,
40766     padWidth: 10,
40767     
40768     
40769     tag: 'div',
40770     cls: '',
40771     bricks: null, //CompositeElement
40772     cols : 0, // array?
40773     // element : null, // wrapped now this.el
40774     _isLayoutInited : null, 
40775     
40776     
40777     getAutoCreate : function(){
40778         
40779         var cfg = {
40780             tag: this.tag,
40781             cls: 'blog-masonary-wrapper ' + this.cls,
40782             cn : {
40783                 cls : 'mas-boxes masonary'
40784             }
40785         };
40786         
40787         return cfg;
40788     },
40789     
40790     getChildContainer: function( )
40791     {
40792         if (this.boxesEl) {
40793             return this.boxesEl;
40794         }
40795         
40796         this.boxesEl = this.el.select('.mas-boxes').first();
40797         
40798         return this.boxesEl;
40799     },
40800     
40801     
40802     initEvents : function()
40803     {
40804         var _this = this;
40805         
40806         if(this.isAutoInitial){
40807             Roo.log('hook children rendered');
40808             this.on('childrenrendered', function() {
40809                 Roo.log('children rendered');
40810                 _this.initial();
40811             } ,this);
40812         }
40813         
40814     },
40815     
40816     initial : function()
40817     {
40818         this.reloadItems();
40819
40820         this.currentSize = this.el.getBox(true);
40821
40822         /// was window resize... - let's see if this works..
40823         Roo.EventManager.onWindowResize(this.resize, this); 
40824
40825         if(!this.isAutoInitial){
40826             this.layout();
40827             return;
40828         }
40829         
40830         this.layout.defer(500,this);
40831     },
40832     
40833     reloadItems: function()
40834     {
40835         this.bricks = this.el.select('.masonry-brick', true);
40836         
40837         this.bricks.each(function(b) {
40838             //Roo.log(b.getSize());
40839             if (!b.attr('originalwidth')) {
40840                 b.attr('originalwidth',  b.getSize().width);
40841             }
40842             
40843         });
40844         
40845         Roo.log(this.bricks.elements.length);
40846     },
40847     
40848     resize : function()
40849     {
40850         Roo.log('resize');
40851         var cs = this.el.getBox(true);
40852         
40853         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40854             Roo.log("no change in with or X");
40855             return;
40856         }
40857         this.currentSize = cs;
40858         this.layout();
40859     },
40860     
40861     layout : function()
40862     {
40863          Roo.log('layout');
40864         this._resetLayout();
40865         //this._manageStamps();
40866       
40867         // don't animate first layout
40868         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40869         this.layoutItems( isInstant );
40870       
40871         // flag for initalized
40872         this._isLayoutInited = true;
40873     },
40874     
40875     layoutItems : function( isInstant )
40876     {
40877         //var items = this._getItemsForLayout( this.items );
40878         // original code supports filtering layout items.. we just ignore it..
40879         
40880         this._layoutItems( this.bricks , isInstant );
40881       
40882         this._postLayout();
40883     },
40884     _layoutItems : function ( items , isInstant)
40885     {
40886        //this.fireEvent( 'layout', this, items );
40887     
40888
40889         if ( !items || !items.elements.length ) {
40890           // no items, emit event with empty array
40891             return;
40892         }
40893
40894         var queue = [];
40895         items.each(function(item) {
40896             Roo.log("layout item");
40897             Roo.log(item);
40898             // get x/y object from method
40899             var position = this._getItemLayoutPosition( item );
40900             // enqueue
40901             position.item = item;
40902             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40903             queue.push( position );
40904         }, this);
40905       
40906         this._processLayoutQueue( queue );
40907     },
40908     /** Sets position of item in DOM
40909     * @param {Element} item
40910     * @param {Number} x - horizontal position
40911     * @param {Number} y - vertical position
40912     * @param {Boolean} isInstant - disables transitions
40913     */
40914     _processLayoutQueue : function( queue )
40915     {
40916         for ( var i=0, len = queue.length; i < len; i++ ) {
40917             var obj = queue[i];
40918             obj.item.position('absolute');
40919             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40920         }
40921     },
40922       
40923     
40924     /**
40925     * Any logic you want to do after each layout,
40926     * i.e. size the container
40927     */
40928     _postLayout : function()
40929     {
40930         this.resizeContainer();
40931     },
40932     
40933     resizeContainer : function()
40934     {
40935         if ( !this.isResizingContainer ) {
40936             return;
40937         }
40938         var size = this._getContainerSize();
40939         if ( size ) {
40940             this.el.setSize(size.width,size.height);
40941             this.boxesEl.setSize(size.width,size.height);
40942         }
40943     },
40944     
40945     
40946     
40947     _resetLayout : function()
40948     {
40949         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40950         this.colWidth = this.el.getWidth();
40951         //this.gutter = this.el.getWidth(); 
40952         
40953         this.measureColumns();
40954
40955         // reset column Y
40956         var i = this.cols;
40957         this.colYs = [];
40958         while (i--) {
40959             this.colYs.push( 0 );
40960         }
40961     
40962         this.maxY = 0;
40963     },
40964
40965     measureColumns : function()
40966     {
40967         this.getContainerWidth();
40968       // if columnWidth is 0, default to outerWidth of first item
40969         if ( !this.columnWidth ) {
40970             var firstItem = this.bricks.first();
40971             Roo.log(firstItem);
40972             this.columnWidth  = this.containerWidth;
40973             if (firstItem && firstItem.attr('originalwidth') ) {
40974                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40975             }
40976             // columnWidth fall back to item of first element
40977             Roo.log("set column width?");
40978                         this.initialColumnWidth = this.columnWidth  ;
40979
40980             // if first elem has no width, default to size of container
40981             
40982         }
40983         
40984         
40985         if (this.initialColumnWidth) {
40986             this.columnWidth = this.initialColumnWidth;
40987         }
40988         
40989         
40990             
40991         // column width is fixed at the top - however if container width get's smaller we should
40992         // reduce it...
40993         
40994         // this bit calcs how man columns..
40995             
40996         var columnWidth = this.columnWidth += this.gutter;
40997       
40998         // calculate columns
40999         var containerWidth = this.containerWidth + this.gutter;
41000         
41001         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
41002         // fix rounding errors, typically with gutters
41003         var excess = columnWidth - containerWidth % columnWidth;
41004         
41005         
41006         // if overshoot is less than a pixel, round up, otherwise floor it
41007         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
41008         cols = Math[ mathMethod ]( cols );
41009         this.cols = Math.max( cols, 1 );
41010         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
41011         
41012          // padding positioning..
41013         var totalColWidth = this.cols * this.columnWidth;
41014         var padavail = this.containerWidth - totalColWidth;
41015         // so for 2 columns - we need 3 'pads'
41016         
41017         var padNeeded = (1+this.cols) * this.padWidth;
41018         
41019         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
41020         
41021         this.columnWidth += padExtra
41022         //this.padWidth = Math.floor(padavail /  ( this.cols));
41023         
41024         // adjust colum width so that padding is fixed??
41025         
41026         // we have 3 columns ... total = width * 3
41027         // we have X left over... that should be used by 
41028         
41029         //if (this.expandC) {
41030             
41031         //}
41032         
41033         
41034         
41035     },
41036     
41037     getContainerWidth : function()
41038     {
41039        /* // container is parent if fit width
41040         var container = this.isFitWidth ? this.element.parentNode : this.element;
41041         // check that this.size and size are there
41042         // IE8 triggers resize on body size change, so they might not be
41043         
41044         var size = getSize( container );  //FIXME
41045         this.containerWidth = size && size.innerWidth; //FIXME
41046         */
41047          
41048         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
41049         
41050     },
41051     
41052     _getItemLayoutPosition : function( item )  // what is item?
41053     {
41054         // we resize the item to our columnWidth..
41055       
41056         item.setWidth(this.columnWidth);
41057         item.autoBoxAdjust  = false;
41058         
41059         var sz = item.getSize();
41060  
41061         // how many columns does this brick span
41062         var remainder = this.containerWidth % this.columnWidth;
41063         
41064         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
41065         // round if off by 1 pixel, otherwise use ceil
41066         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
41067         colSpan = Math.min( colSpan, this.cols );
41068         
41069         // normally this should be '1' as we dont' currently allow multi width columns..
41070         
41071         var colGroup = this._getColGroup( colSpan );
41072         // get the minimum Y value from the columns
41073         var minimumY = Math.min.apply( Math, colGroup );
41074         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
41075         
41076         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
41077          
41078         // position the brick
41079         var position = {
41080             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
41081             y: this.currentSize.y + minimumY + this.padHeight
41082         };
41083         
41084         Roo.log(position);
41085         // apply setHeight to necessary columns
41086         var setHeight = minimumY + sz.height + this.padHeight;
41087         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
41088         
41089         var setSpan = this.cols + 1 - colGroup.length;
41090         for ( var i = 0; i < setSpan; i++ ) {
41091           this.colYs[ shortColIndex + i ] = setHeight ;
41092         }
41093       
41094         return position;
41095     },
41096     
41097     /**
41098      * @param {Number} colSpan - number of columns the element spans
41099      * @returns {Array} colGroup
41100      */
41101     _getColGroup : function( colSpan )
41102     {
41103         if ( colSpan < 2 ) {
41104           // if brick spans only one column, use all the column Ys
41105           return this.colYs;
41106         }
41107       
41108         var colGroup = [];
41109         // how many different places could this brick fit horizontally
41110         var groupCount = this.cols + 1 - colSpan;
41111         // for each group potential horizontal position
41112         for ( var i = 0; i < groupCount; i++ ) {
41113           // make an array of colY values for that one group
41114           var groupColYs = this.colYs.slice( i, i + colSpan );
41115           // and get the max value of the array
41116           colGroup[i] = Math.max.apply( Math, groupColYs );
41117         }
41118         return colGroup;
41119     },
41120     /*
41121     _manageStamp : function( stamp )
41122     {
41123         var stampSize =  stamp.getSize();
41124         var offset = stamp.getBox();
41125         // get the columns that this stamp affects
41126         var firstX = this.isOriginLeft ? offset.x : offset.right;
41127         var lastX = firstX + stampSize.width;
41128         var firstCol = Math.floor( firstX / this.columnWidth );
41129         firstCol = Math.max( 0, firstCol );
41130         
41131         var lastCol = Math.floor( lastX / this.columnWidth );
41132         // lastCol should not go over if multiple of columnWidth #425
41133         lastCol -= lastX % this.columnWidth ? 0 : 1;
41134         lastCol = Math.min( this.cols - 1, lastCol );
41135         
41136         // set colYs to bottom of the stamp
41137         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
41138             stampSize.height;
41139             
41140         for ( var i = firstCol; i <= lastCol; i++ ) {
41141           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
41142         }
41143     },
41144     */
41145     
41146     _getContainerSize : function()
41147     {
41148         this.maxY = Math.max.apply( Math, this.colYs );
41149         var size = {
41150             height: this.maxY
41151         };
41152       
41153         if ( this.isFitWidth ) {
41154             size.width = this._getContainerFitWidth();
41155         }
41156       
41157         return size;
41158     },
41159     
41160     _getContainerFitWidth : function()
41161     {
41162         var unusedCols = 0;
41163         // count unused columns
41164         var i = this.cols;
41165         while ( --i ) {
41166           if ( this.colYs[i] !== 0 ) {
41167             break;
41168           }
41169           unusedCols++;
41170         }
41171         // fit container to columns that have been used
41172         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
41173     },
41174     
41175     needsResizeLayout : function()
41176     {
41177         var previousWidth = this.containerWidth;
41178         this.getContainerWidth();
41179         return previousWidth !== this.containerWidth;
41180     }
41181  
41182 });
41183
41184  
41185
41186  /*
41187  * - LGPL
41188  *
41189  * element
41190  * 
41191  */
41192
41193 /**
41194  * @class Roo.bootstrap.MasonryBrick
41195  * @extends Roo.bootstrap.Component
41196  * Bootstrap MasonryBrick class
41197  * 
41198  * @constructor
41199  * Create a new MasonryBrick
41200  * @param {Object} config The config object
41201  */
41202
41203 Roo.bootstrap.MasonryBrick = function(config){
41204     
41205     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
41206     
41207     Roo.bootstrap.MasonryBrick.register(this);
41208     
41209     this.addEvents({
41210         // raw events
41211         /**
41212          * @event click
41213          * When a MasonryBrick is clcik
41214          * @param {Roo.bootstrap.MasonryBrick} this
41215          * @param {Roo.EventObject} e
41216          */
41217         "click" : true
41218     });
41219 };
41220
41221 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
41222     
41223     /**
41224      * @cfg {String} title
41225      */   
41226     title : '',
41227     /**
41228      * @cfg {String} html
41229      */   
41230     html : '',
41231     /**
41232      * @cfg {String} bgimage
41233      */   
41234     bgimage : '',
41235     /**
41236      * @cfg {String} videourl
41237      */   
41238     videourl : '',
41239     /**
41240      * @cfg {String} cls
41241      */   
41242     cls : '',
41243     /**
41244      * @cfg {String} href
41245      */   
41246     href : '',
41247     /**
41248      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
41249      */   
41250     size : 'xs',
41251     
41252     /**
41253      * @cfg {String} placetitle (center|bottom)
41254      */   
41255     placetitle : '',
41256     
41257     /**
41258      * @cfg {Boolean} isFitContainer defalut true
41259      */   
41260     isFitContainer : true, 
41261     
41262     /**
41263      * @cfg {Boolean} preventDefault defalut false
41264      */   
41265     preventDefault : false, 
41266     
41267     /**
41268      * @cfg {Boolean} inverse defalut false
41269      */   
41270     maskInverse : false, 
41271     
41272     getAutoCreate : function()
41273     {
41274         if(!this.isFitContainer){
41275             return this.getSplitAutoCreate();
41276         }
41277         
41278         var cls = 'masonry-brick masonry-brick-full';
41279         
41280         if(this.href.length){
41281             cls += ' masonry-brick-link';
41282         }
41283         
41284         if(this.bgimage.length){
41285             cls += ' masonry-brick-image';
41286         }
41287         
41288         if(this.maskInverse){
41289             cls += ' mask-inverse';
41290         }
41291         
41292         if(!this.html.length && !this.maskInverse && !this.videourl.length){
41293             cls += ' enable-mask';
41294         }
41295         
41296         if(this.size){
41297             cls += ' masonry-' + this.size + '-brick';
41298         }
41299         
41300         if(this.placetitle.length){
41301             
41302             switch (this.placetitle) {
41303                 case 'center' :
41304                     cls += ' masonry-center-title';
41305                     break;
41306                 case 'bottom' :
41307                     cls += ' masonry-bottom-title';
41308                     break;
41309                 default:
41310                     break;
41311             }
41312             
41313         } else {
41314             if(!this.html.length && !this.bgimage.length){
41315                 cls += ' masonry-center-title';
41316             }
41317
41318             if(!this.html.length && this.bgimage.length){
41319                 cls += ' masonry-bottom-title';
41320             }
41321         }
41322         
41323         if(this.cls){
41324             cls += ' ' + this.cls;
41325         }
41326         
41327         var cfg = {
41328             tag: (this.href.length) ? 'a' : 'div',
41329             cls: cls,
41330             cn: [
41331                 {
41332                     tag: 'div',
41333                     cls: 'masonry-brick-mask'
41334                 },
41335                 {
41336                     tag: 'div',
41337                     cls: 'masonry-brick-paragraph',
41338                     cn: []
41339                 }
41340             ]
41341         };
41342         
41343         if(this.href.length){
41344             cfg.href = this.href;
41345         }
41346         
41347         var cn = cfg.cn[1].cn;
41348         
41349         if(this.title.length){
41350             cn.push({
41351                 tag: 'h4',
41352                 cls: 'masonry-brick-title',
41353                 html: this.title
41354             });
41355         }
41356         
41357         if(this.html.length){
41358             cn.push({
41359                 tag: 'p',
41360                 cls: 'masonry-brick-text',
41361                 html: this.html
41362             });
41363         }
41364         
41365         if (!this.title.length && !this.html.length) {
41366             cfg.cn[1].cls += ' hide';
41367         }
41368         
41369         if(this.bgimage.length){
41370             cfg.cn.push({
41371                 tag: 'img',
41372                 cls: 'masonry-brick-image-view',
41373                 src: this.bgimage
41374             });
41375         }
41376         
41377         if(this.videourl.length){
41378             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41379             // youtube support only?
41380             cfg.cn.push({
41381                 tag: 'iframe',
41382                 cls: 'masonry-brick-image-view',
41383                 src: vurl,
41384                 frameborder : 0,
41385                 allowfullscreen : true
41386             });
41387         }
41388         
41389         return cfg;
41390         
41391     },
41392     
41393     getSplitAutoCreate : function()
41394     {
41395         var cls = 'masonry-brick masonry-brick-split';
41396         
41397         if(this.href.length){
41398             cls += ' masonry-brick-link';
41399         }
41400         
41401         if(this.bgimage.length){
41402             cls += ' masonry-brick-image';
41403         }
41404         
41405         if(this.size){
41406             cls += ' masonry-' + this.size + '-brick';
41407         }
41408         
41409         switch (this.placetitle) {
41410             case 'center' :
41411                 cls += ' masonry-center-title';
41412                 break;
41413             case 'bottom' :
41414                 cls += ' masonry-bottom-title';
41415                 break;
41416             default:
41417                 if(!this.bgimage.length){
41418                     cls += ' masonry-center-title';
41419                 }
41420
41421                 if(this.bgimage.length){
41422                     cls += ' masonry-bottom-title';
41423                 }
41424                 break;
41425         }
41426         
41427         if(this.cls){
41428             cls += ' ' + this.cls;
41429         }
41430         
41431         var cfg = {
41432             tag: (this.href.length) ? 'a' : 'div',
41433             cls: cls,
41434             cn: [
41435                 {
41436                     tag: 'div',
41437                     cls: 'masonry-brick-split-head',
41438                     cn: [
41439                         {
41440                             tag: 'div',
41441                             cls: 'masonry-brick-paragraph',
41442                             cn: []
41443                         }
41444                     ]
41445                 },
41446                 {
41447                     tag: 'div',
41448                     cls: 'masonry-brick-split-body',
41449                     cn: []
41450                 }
41451             ]
41452         };
41453         
41454         if(this.href.length){
41455             cfg.href = this.href;
41456         }
41457         
41458         if(this.title.length){
41459             cfg.cn[0].cn[0].cn.push({
41460                 tag: 'h4',
41461                 cls: 'masonry-brick-title',
41462                 html: this.title
41463             });
41464         }
41465         
41466         if(this.html.length){
41467             cfg.cn[1].cn.push({
41468                 tag: 'p',
41469                 cls: 'masonry-brick-text',
41470                 html: this.html
41471             });
41472         }
41473
41474         if(this.bgimage.length){
41475             cfg.cn[0].cn.push({
41476                 tag: 'img',
41477                 cls: 'masonry-brick-image-view',
41478                 src: this.bgimage
41479             });
41480         }
41481         
41482         if(this.videourl.length){
41483             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41484             // youtube support only?
41485             cfg.cn[0].cn.cn.push({
41486                 tag: 'iframe',
41487                 cls: 'masonry-brick-image-view',
41488                 src: vurl,
41489                 frameborder : 0,
41490                 allowfullscreen : true
41491             });
41492         }
41493         
41494         return cfg;
41495     },
41496     
41497     initEvents: function() 
41498     {
41499         switch (this.size) {
41500             case 'xs' :
41501                 this.x = 1;
41502                 this.y = 1;
41503                 break;
41504             case 'sm' :
41505                 this.x = 2;
41506                 this.y = 2;
41507                 break;
41508             case 'md' :
41509             case 'md-left' :
41510             case 'md-right' :
41511                 this.x = 3;
41512                 this.y = 3;
41513                 break;
41514             case 'tall' :
41515                 this.x = 2;
41516                 this.y = 3;
41517                 break;
41518             case 'wide' :
41519                 this.x = 3;
41520                 this.y = 2;
41521                 break;
41522             case 'wide-thin' :
41523                 this.x = 3;
41524                 this.y = 1;
41525                 break;
41526                         
41527             default :
41528                 break;
41529         }
41530         
41531         if(Roo.isTouch){
41532             this.el.on('touchstart', this.onTouchStart, this);
41533             this.el.on('touchmove', this.onTouchMove, this);
41534             this.el.on('touchend', this.onTouchEnd, this);
41535             this.el.on('contextmenu', this.onContextMenu, this);
41536         } else {
41537             this.el.on('mouseenter'  ,this.enter, this);
41538             this.el.on('mouseleave', this.leave, this);
41539             this.el.on('click', this.onClick, this);
41540         }
41541         
41542         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41543             this.parent().bricks.push(this);   
41544         }
41545         
41546     },
41547     
41548     onClick: function(e, el)
41549     {
41550         var time = this.endTimer - this.startTimer;
41551         // Roo.log(e.preventDefault());
41552         if(Roo.isTouch){
41553             if(time > 1000){
41554                 e.preventDefault();
41555                 return;
41556             }
41557         }
41558         
41559         if(!this.preventDefault){
41560             return;
41561         }
41562         
41563         e.preventDefault();
41564         
41565         if (this.activeClass != '') {
41566             this.selectBrick();
41567         }
41568         
41569         this.fireEvent('click', this, e);
41570     },
41571     
41572     enter: function(e, el)
41573     {
41574         e.preventDefault();
41575         
41576         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41577             return;
41578         }
41579         
41580         if(this.bgimage.length && this.html.length){
41581             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41582         }
41583     },
41584     
41585     leave: function(e, el)
41586     {
41587         e.preventDefault();
41588         
41589         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
41590             return;
41591         }
41592         
41593         if(this.bgimage.length && this.html.length){
41594             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41595         }
41596     },
41597     
41598     onTouchStart: function(e, el)
41599     {
41600 //        e.preventDefault();
41601         
41602         this.touchmoved = false;
41603         
41604         if(!this.isFitContainer){
41605             return;
41606         }
41607         
41608         if(!this.bgimage.length || !this.html.length){
41609             return;
41610         }
41611         
41612         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41613         
41614         this.timer = new Date().getTime();
41615         
41616     },
41617     
41618     onTouchMove: function(e, el)
41619     {
41620         this.touchmoved = true;
41621     },
41622     
41623     onContextMenu : function(e,el)
41624     {
41625         e.preventDefault();
41626         e.stopPropagation();
41627         return false;
41628     },
41629     
41630     onTouchEnd: function(e, el)
41631     {
41632 //        e.preventDefault();
41633         
41634         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41635         
41636             this.leave(e,el);
41637             
41638             return;
41639         }
41640         
41641         if(!this.bgimage.length || !this.html.length){
41642             
41643             if(this.href.length){
41644                 window.location.href = this.href;
41645             }
41646             
41647             return;
41648         }
41649         
41650         if(!this.isFitContainer){
41651             return;
41652         }
41653         
41654         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41655         
41656         window.location.href = this.href;
41657     },
41658     
41659     //selection on single brick only
41660     selectBrick : function() {
41661         
41662         if (!this.parentId) {
41663             return;
41664         }
41665         
41666         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41667         var index = m.selectedBrick.indexOf(this.id);
41668         
41669         if ( index > -1) {
41670             m.selectedBrick.splice(index,1);
41671             this.el.removeClass(this.activeClass);
41672             return;
41673         }
41674         
41675         for(var i = 0; i < m.selectedBrick.length; i++) {
41676             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41677             b.el.removeClass(b.activeClass);
41678         }
41679         
41680         m.selectedBrick = [];
41681         
41682         m.selectedBrick.push(this.id);
41683         this.el.addClass(this.activeClass);
41684         return;
41685     },
41686     
41687     isSelected : function(){
41688         return this.el.hasClass(this.activeClass);
41689         
41690     }
41691 });
41692
41693 Roo.apply(Roo.bootstrap.MasonryBrick, {
41694     
41695     //groups: {},
41696     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41697      /**
41698     * register a Masonry Brick
41699     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41700     */
41701     
41702     register : function(brick)
41703     {
41704         //this.groups[brick.id] = brick;
41705         this.groups.add(brick.id, brick);
41706     },
41707     /**
41708     * fetch a  masonry brick based on the masonry brick ID
41709     * @param {string} the masonry brick to add
41710     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41711     */
41712     
41713     get: function(brick_id) 
41714     {
41715         // if (typeof(this.groups[brick_id]) == 'undefined') {
41716         //     return false;
41717         // }
41718         // return this.groups[brick_id] ;
41719         
41720         if(this.groups.key(brick_id)) {
41721             return this.groups.key(brick_id);
41722         }
41723         
41724         return false;
41725     }
41726     
41727     
41728     
41729 });
41730
41731  /*
41732  * - LGPL
41733  *
41734  * element
41735  * 
41736  */
41737
41738 /**
41739  * @class Roo.bootstrap.Brick
41740  * @extends Roo.bootstrap.Component
41741  * Bootstrap Brick class
41742  * 
41743  * @constructor
41744  * Create a new Brick
41745  * @param {Object} config The config object
41746  */
41747
41748 Roo.bootstrap.Brick = function(config){
41749     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41750     
41751     this.addEvents({
41752         // raw events
41753         /**
41754          * @event click
41755          * When a Brick is click
41756          * @param {Roo.bootstrap.Brick} this
41757          * @param {Roo.EventObject} e
41758          */
41759         "click" : true
41760     });
41761 };
41762
41763 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41764     
41765     /**
41766      * @cfg {String} title
41767      */   
41768     title : '',
41769     /**
41770      * @cfg {String} html
41771      */   
41772     html : '',
41773     /**
41774      * @cfg {String} bgimage
41775      */   
41776     bgimage : '',
41777     /**
41778      * @cfg {String} cls
41779      */   
41780     cls : '',
41781     /**
41782      * @cfg {String} href
41783      */   
41784     href : '',
41785     /**
41786      * @cfg {String} video
41787      */   
41788     video : '',
41789     /**
41790      * @cfg {Boolean} square
41791      */   
41792     square : true,
41793     
41794     getAutoCreate : function()
41795     {
41796         var cls = 'roo-brick';
41797         
41798         if(this.href.length){
41799             cls += ' roo-brick-link';
41800         }
41801         
41802         if(this.bgimage.length){
41803             cls += ' roo-brick-image';
41804         }
41805         
41806         if(!this.html.length && !this.bgimage.length){
41807             cls += ' roo-brick-center-title';
41808         }
41809         
41810         if(!this.html.length && this.bgimage.length){
41811             cls += ' roo-brick-bottom-title';
41812         }
41813         
41814         if(this.cls){
41815             cls += ' ' + this.cls;
41816         }
41817         
41818         var cfg = {
41819             tag: (this.href.length) ? 'a' : 'div',
41820             cls: cls,
41821             cn: [
41822                 {
41823                     tag: 'div',
41824                     cls: 'roo-brick-paragraph',
41825                     cn: []
41826                 }
41827             ]
41828         };
41829         
41830         if(this.href.length){
41831             cfg.href = this.href;
41832         }
41833         
41834         var cn = cfg.cn[0].cn;
41835         
41836         if(this.title.length){
41837             cn.push({
41838                 tag: 'h4',
41839                 cls: 'roo-brick-title',
41840                 html: this.title
41841             });
41842         }
41843         
41844         if(this.html.length){
41845             cn.push({
41846                 tag: 'p',
41847                 cls: 'roo-brick-text',
41848                 html: this.html
41849             });
41850         } else {
41851             cn.cls += ' hide';
41852         }
41853         
41854         if(this.bgimage.length){
41855             cfg.cn.push({
41856                 tag: 'img',
41857                 cls: 'roo-brick-image-view',
41858                 src: this.bgimage
41859             });
41860         }
41861         
41862         return cfg;
41863     },
41864     
41865     initEvents: function() 
41866     {
41867         if(this.title.length || this.html.length){
41868             this.el.on('mouseenter'  ,this.enter, this);
41869             this.el.on('mouseleave', this.leave, this);
41870         }
41871         
41872         Roo.EventManager.onWindowResize(this.resize, this); 
41873         
41874         if(this.bgimage.length){
41875             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41876             this.imageEl.on('load', this.onImageLoad, this);
41877             return;
41878         }
41879         
41880         this.resize();
41881     },
41882     
41883     onImageLoad : function()
41884     {
41885         this.resize();
41886     },
41887     
41888     resize : function()
41889     {
41890         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41891         
41892         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41893         
41894         if(this.bgimage.length){
41895             var image = this.el.select('.roo-brick-image-view', true).first();
41896             
41897             image.setWidth(paragraph.getWidth());
41898             
41899             if(this.square){
41900                 image.setHeight(paragraph.getWidth());
41901             }
41902             
41903             this.el.setHeight(image.getHeight());
41904             paragraph.setHeight(image.getHeight());
41905             
41906         }
41907         
41908     },
41909     
41910     enter: function(e, el)
41911     {
41912         e.preventDefault();
41913         
41914         if(this.bgimage.length){
41915             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41916             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41917         }
41918     },
41919     
41920     leave: function(e, el)
41921     {
41922         e.preventDefault();
41923         
41924         if(this.bgimage.length){
41925             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41926             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41927         }
41928     }
41929     
41930 });
41931
41932  
41933
41934  /*
41935  * - LGPL
41936  *
41937  * Number field 
41938  */
41939
41940 /**
41941  * @class Roo.bootstrap.form.NumberField
41942  * @extends Roo.bootstrap.form.Input
41943  * Bootstrap NumberField class
41944  * 
41945  * 
41946  * 
41947  * 
41948  * @constructor
41949  * Create a new NumberField
41950  * @param {Object} config The config object
41951  */
41952
41953 Roo.bootstrap.form.NumberField = function(config){
41954     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41955 };
41956
41957 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41958     
41959     /**
41960      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41961      */
41962     allowDecimals : true,
41963     /**
41964      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41965      */
41966     decimalSeparator : ".",
41967     /**
41968      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41969      */
41970     decimalPrecision : 2,
41971     /**
41972      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41973      */
41974     allowNegative : true,
41975     
41976     /**
41977      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41978      */
41979     allowZero: true,
41980     /**
41981      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41982      */
41983     minValue : Number.NEGATIVE_INFINITY,
41984     /**
41985      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41986      */
41987     maxValue : Number.MAX_VALUE,
41988     /**
41989      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41990      */
41991     minText : "The minimum value for this field is {0}",
41992     /**
41993      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41994      */
41995     maxText : "The maximum value for this field is {0}",
41996     /**
41997      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41998      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41999      */
42000     nanText : "{0} is not a valid number",
42001     /**
42002      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42003      */
42004     thousandsDelimiter : false,
42005     /**
42006      * @cfg {String} valueAlign alignment of value
42007      */
42008     valueAlign : "left",
42009
42010     getAutoCreate : function()
42011     {
42012         var hiddenInput = {
42013             tag: 'input',
42014             type: 'hidden',
42015             id: Roo.id(),
42016             cls: 'hidden-number-input'
42017         };
42018         
42019         if (this.name) {
42020             hiddenInput.name = this.name;
42021         }
42022         
42023         this.name = '';
42024         
42025         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
42026         
42027         this.name = hiddenInput.name;
42028         
42029         if(cfg.cn.length > 0) {
42030             cfg.cn.push(hiddenInput);
42031         }
42032         
42033         return cfg;
42034     },
42035
42036     // private
42037     initEvents : function()
42038     {   
42039         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
42040         
42041         var allowed = "0123456789";
42042         
42043         if(this.allowDecimals){
42044             allowed += this.decimalSeparator;
42045         }
42046         
42047         if(this.allowNegative){
42048             allowed += "-";
42049         }
42050         
42051         if(this.thousandsDelimiter) {
42052             allowed += ",";
42053         }
42054         
42055         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42056         
42057         var keyPress = function(e){
42058             
42059             var k = e.getKey();
42060             
42061             var c = e.getCharCode();
42062             
42063             if(
42064                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42065                     allowed.indexOf(String.fromCharCode(c)) === -1
42066             ){
42067                 e.stopEvent();
42068                 return;
42069             }
42070             
42071             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42072                 return;
42073             }
42074             
42075             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42076                 e.stopEvent();
42077             }
42078         };
42079         
42080         this.el.on("keypress", keyPress, this);
42081     },
42082     
42083     validateValue : function(value)
42084     {
42085         
42086         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
42087             return false;
42088         }
42089         
42090         var num = this.parseValue(value);
42091         
42092         if(isNaN(num)){
42093             this.markInvalid(String.format(this.nanText, value));
42094             return false;
42095         }
42096         
42097         if(num < this.minValue){
42098             this.markInvalid(String.format(this.minText, this.minValue));
42099             return false;
42100         }
42101         
42102         if(num > this.maxValue){
42103             this.markInvalid(String.format(this.maxText, this.maxValue));
42104             return false;
42105         }
42106         
42107         return true;
42108     },
42109
42110     getValue : function()
42111     {
42112         var v = this.hiddenEl().getValue();
42113         
42114         return this.fixPrecision(this.parseValue(v));
42115     },
42116
42117     parseValue : function(value)
42118     {
42119         if(this.thousandsDelimiter) {
42120             value += "";
42121             r = new RegExp(",", "g");
42122             value = value.replace(r, "");
42123         }
42124         
42125         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42126         return isNaN(value) ? '' : value;
42127     },
42128
42129     fixPrecision : function(value)
42130     {
42131         if(this.thousandsDelimiter) {
42132             value += "";
42133             r = new RegExp(",", "g");
42134             value = value.replace(r, "");
42135         }
42136         
42137         var nan = isNaN(value);
42138         
42139         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42140             return nan ? '' : value;
42141         }
42142         return parseFloat(value).toFixed(this.decimalPrecision);
42143     },
42144
42145     setValue : function(v)
42146     {
42147         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42148         
42149         this.value = v;
42150         
42151         if(this.rendered){
42152             
42153             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42154             
42155             this.inputEl().dom.value = (v == '') ? '' :
42156                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42157             
42158             if(!this.allowZero && v === '0') {
42159                 this.hiddenEl().dom.value = '';
42160                 this.inputEl().dom.value = '';
42161             }
42162             
42163             this.validate();
42164         }
42165     },
42166
42167     decimalPrecisionFcn : function(v)
42168     {
42169         return Math.floor(v);
42170     },
42171
42172     beforeBlur : function()
42173     {
42174         var v = this.parseValue(this.getRawValue());
42175         
42176         if(v || v === 0 || v === ''){
42177             this.setValue(v);
42178         }
42179     },
42180     
42181     hiddenEl : function()
42182     {
42183         return this.el.select('input.hidden-number-input',true).first();
42184     }
42185     
42186 });
42187
42188  
42189
42190 /*
42191 * Licence: LGPL
42192 */
42193
42194 /**
42195  * @class Roo.bootstrap.DocumentSlider
42196  * @extends Roo.bootstrap.Component
42197  * Bootstrap DocumentSlider class
42198  * 
42199  * @constructor
42200  * Create a new DocumentViewer
42201  * @param {Object} config The config object
42202  */
42203
42204 Roo.bootstrap.DocumentSlider = function(config){
42205     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
42206     
42207     this.files = [];
42208     
42209     this.addEvents({
42210         /**
42211          * @event initial
42212          * Fire after initEvent
42213          * @param {Roo.bootstrap.DocumentSlider} this
42214          */
42215         "initial" : true,
42216         /**
42217          * @event update
42218          * Fire after update
42219          * @param {Roo.bootstrap.DocumentSlider} this
42220          */
42221         "update" : true,
42222         /**
42223          * @event click
42224          * Fire after click
42225          * @param {Roo.bootstrap.DocumentSlider} this
42226          */
42227         "click" : true
42228     });
42229 };
42230
42231 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
42232     
42233     files : false,
42234     
42235     indicator : 0,
42236     
42237     getAutoCreate : function()
42238     {
42239         var cfg = {
42240             tag : 'div',
42241             cls : 'roo-document-slider',
42242             cn : [
42243                 {
42244                     tag : 'div',
42245                     cls : 'roo-document-slider-header',
42246                     cn : [
42247                         {
42248                             tag : 'div',
42249                             cls : 'roo-document-slider-header-title'
42250                         }
42251                     ]
42252                 },
42253                 {
42254                     tag : 'div',
42255                     cls : 'roo-document-slider-body',
42256                     cn : [
42257                         {
42258                             tag : 'div',
42259                             cls : 'roo-document-slider-prev',
42260                             cn : [
42261                                 {
42262                                     tag : 'i',
42263                                     cls : 'fa fa-chevron-left'
42264                                 }
42265                             ]
42266                         },
42267                         {
42268                             tag : 'div',
42269                             cls : 'roo-document-slider-thumb',
42270                             cn : [
42271                                 {
42272                                     tag : 'img',
42273                                     cls : 'roo-document-slider-image'
42274                                 }
42275                             ]
42276                         },
42277                         {
42278                             tag : 'div',
42279                             cls : 'roo-document-slider-next',
42280                             cn : [
42281                                 {
42282                                     tag : 'i',
42283                                     cls : 'fa fa-chevron-right'
42284                                 }
42285                             ]
42286                         }
42287                     ]
42288                 }
42289             ]
42290         };
42291         
42292         return cfg;
42293     },
42294     
42295     initEvents : function()
42296     {
42297         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
42298         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
42299         
42300         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
42301         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
42302         
42303         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
42304         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
42305         
42306         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
42307         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
42308         
42309         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
42310         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
42311         
42312         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
42313         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
42314         
42315         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
42316         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
42317         
42318         this.thumbEl.on('click', this.onClick, this);
42319         
42320         this.prevIndicator.on('click', this.prev, this);
42321         
42322         this.nextIndicator.on('click', this.next, this);
42323         
42324     },
42325     
42326     initial : function()
42327     {
42328         if(this.files.length){
42329             this.indicator = 1;
42330             this.update()
42331         }
42332         
42333         this.fireEvent('initial', this);
42334     },
42335     
42336     update : function()
42337     {
42338         this.imageEl.attr('src', this.files[this.indicator - 1]);
42339         
42340         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42341         
42342         this.prevIndicator.show();
42343         
42344         if(this.indicator == 1){
42345             this.prevIndicator.hide();
42346         }
42347         
42348         this.nextIndicator.show();
42349         
42350         if(this.indicator == this.files.length){
42351             this.nextIndicator.hide();
42352         }
42353         
42354         this.thumbEl.scrollTo('top');
42355         
42356         this.fireEvent('update', this);
42357     },
42358     
42359     onClick : function(e)
42360     {
42361         e.preventDefault();
42362         
42363         this.fireEvent('click', this);
42364     },
42365     
42366     prev : function(e)
42367     {
42368         e.preventDefault();
42369         
42370         this.indicator = Math.max(1, this.indicator - 1);
42371         
42372         this.update();
42373     },
42374     
42375     next : function(e)
42376     {
42377         e.preventDefault();
42378         
42379         this.indicator = Math.min(this.files.length, this.indicator + 1);
42380         
42381         this.update();
42382     }
42383 });
42384 /*
42385  * - LGPL
42386  *
42387  * RadioSet
42388  *
42389  *
42390  */
42391
42392 /**
42393  * @class Roo.bootstrap.form.RadioSet
42394  * @extends Roo.bootstrap.form.Input
42395  * @children Roo.bootstrap.form.Radio
42396  * Bootstrap RadioSet class
42397  * @cfg {String} indicatorpos (left|right) default left
42398  * @cfg {Boolean} inline (true|false) inline the element (default true)
42399  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42400  * @constructor
42401  * Create a new RadioSet
42402  * @param {Object} config The config object
42403  */
42404
42405 Roo.bootstrap.form.RadioSet = function(config){
42406     
42407     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42408     
42409     this.radioes = [];
42410     
42411     Roo.bootstrap.form.RadioSet.register(this);
42412     
42413     this.addEvents({
42414         /**
42415         * @event check
42416         * Fires when the element is checked or unchecked.
42417         * @param {Roo.bootstrap.form.RadioSet} this This radio
42418         * @param {Roo.bootstrap.form.Radio} item The checked item
42419         */
42420        check : true,
42421        /**
42422         * @event click
42423         * Fires when the element is click.
42424         * @param {Roo.bootstrap.form.RadioSet} this This radio set
42425         * @param {Roo.bootstrap.form.Radio} item The checked item
42426         * @param {Roo.EventObject} e The event object
42427         */
42428        click : true
42429     });
42430     
42431 };
42432
42433 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
42434
42435     radioes : false,
42436     
42437     inline : true,
42438     
42439     weight : '',
42440     
42441     indicatorpos : 'left',
42442     
42443     getAutoCreate : function()
42444     {
42445         var label = {
42446             tag : 'label',
42447             cls : 'roo-radio-set-label',
42448             cn : [
42449                 {
42450                     tag : 'span',
42451                     html : this.fieldLabel
42452                 }
42453             ]
42454         };
42455         if (Roo.bootstrap.version == 3) {
42456             
42457             
42458             if(this.indicatorpos == 'left'){
42459                 label.cn.unshift({
42460                     tag : 'i',
42461                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42462                     tooltip : 'This field is required'
42463                 });
42464             } else {
42465                 label.cn.push({
42466                     tag : 'i',
42467                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42468                     tooltip : 'This field is required'
42469                 });
42470             }
42471         }
42472         var items = {
42473             tag : 'div',
42474             cls : 'roo-radio-set-items'
42475         };
42476         
42477         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42478         
42479         if (align === 'left' && this.fieldLabel.length) {
42480             
42481             items = {
42482                 cls : "roo-radio-set-right", 
42483                 cn: [
42484                     items
42485                 ]
42486             };
42487             
42488             if(this.labelWidth > 12){
42489                 label.style = "width: " + this.labelWidth + 'px';
42490             }
42491             
42492             if(this.labelWidth < 13 && this.labelmd == 0){
42493                 this.labelmd = this.labelWidth;
42494             }
42495             
42496             if(this.labellg > 0){
42497                 label.cls += ' col-lg-' + this.labellg;
42498                 items.cls += ' col-lg-' + (12 - this.labellg);
42499             }
42500             
42501             if(this.labelmd > 0){
42502                 label.cls += ' col-md-' + this.labelmd;
42503                 items.cls += ' col-md-' + (12 - this.labelmd);
42504             }
42505             
42506             if(this.labelsm > 0){
42507                 label.cls += ' col-sm-' + this.labelsm;
42508                 items.cls += ' col-sm-' + (12 - this.labelsm);
42509             }
42510             
42511             if(this.labelxs > 0){
42512                 label.cls += ' col-xs-' + this.labelxs;
42513                 items.cls += ' col-xs-' + (12 - this.labelxs);
42514             }
42515         }
42516         
42517         var cfg = {
42518             tag : 'div',
42519             cls : 'roo-radio-set',
42520             cn : [
42521                 {
42522                     tag : 'input',
42523                     cls : 'roo-radio-set-input',
42524                     type : 'hidden',
42525                     name : this.name,
42526                     value : this.value ? this.value :  ''
42527                 },
42528                 label,
42529                 items
42530             ]
42531         };
42532         
42533         if(this.weight.length){
42534             cfg.cls += ' roo-radio-' + this.weight;
42535         }
42536         
42537         if(this.inline) {
42538             cfg.cls += ' roo-radio-set-inline';
42539         }
42540         
42541         var settings=this;
42542         ['xs','sm','md','lg'].map(function(size){
42543             if (settings[size]) {
42544                 cfg.cls += ' col-' + size + '-' + settings[size];
42545             }
42546         });
42547         
42548         return cfg;
42549         
42550     },
42551
42552     initEvents : function()
42553     {
42554         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42555         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42556         
42557         if(!this.fieldLabel.length){
42558             this.labelEl.hide();
42559         }
42560         
42561         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42562         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42563         
42564         this.indicator = this.indicatorEl();
42565         
42566         if(this.indicator){
42567             this.indicator.addClass('invisible');
42568         }
42569         
42570         this.originalValue = this.getValue();
42571         
42572     },
42573     
42574     inputEl: function ()
42575     {
42576         return this.el.select('.roo-radio-set-input', true).first();
42577     },
42578     
42579     getChildContainer : function()
42580     {
42581         return this.itemsEl;
42582     },
42583     
42584     register : function(item)
42585     {
42586         this.radioes.push(item);
42587         
42588     },
42589     
42590     validate : function()
42591     {   
42592         if(this.getVisibilityEl().hasClass('hidden')){
42593             return true;
42594         }
42595         
42596         var valid = false;
42597         
42598         Roo.each(this.radioes, function(i){
42599             if(!i.checked){
42600                 return;
42601             }
42602             
42603             valid = true;
42604             return false;
42605         });
42606         
42607         if(this.allowBlank) {
42608             return true;
42609         }
42610         
42611         if(this.disabled || valid){
42612             this.markValid();
42613             return true;
42614         }
42615         
42616         this.markInvalid();
42617         return false;
42618         
42619     },
42620     
42621     markValid : function()
42622     {
42623         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42624             this.indicatorEl().removeClass('visible');
42625             this.indicatorEl().addClass('invisible');
42626         }
42627         
42628         
42629         if (Roo.bootstrap.version == 3) {
42630             this.el.removeClass([this.invalidClass, this.validClass]);
42631             this.el.addClass(this.validClass);
42632         } else {
42633             this.el.removeClass(['is-invalid','is-valid']);
42634             this.el.addClass(['is-valid']);
42635         }
42636         this.fireEvent('valid', this);
42637     },
42638     
42639     markInvalid : function(msg)
42640     {
42641         if(this.allowBlank || this.disabled){
42642             return;
42643         }
42644         
42645         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42646             this.indicatorEl().removeClass('invisible');
42647             this.indicatorEl().addClass('visible');
42648         }
42649         if (Roo.bootstrap.version == 3) {
42650             this.el.removeClass([this.invalidClass, this.validClass]);
42651             this.el.addClass(this.invalidClass);
42652         } else {
42653             this.el.removeClass(['is-invalid','is-valid']);
42654             this.el.addClass(['is-invalid']);
42655         }
42656         
42657         this.fireEvent('invalid', this, msg);
42658         
42659     },
42660     
42661     setValue : function(v, suppressEvent)
42662     {   
42663         if(this.value === v){
42664             return;
42665         }
42666         
42667         this.value = v;
42668         
42669         if(this.rendered){
42670             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42671         }
42672         
42673         Roo.each(this.radioes, function(i){
42674             i.checked = false;
42675             i.el.removeClass('checked');
42676         });
42677         
42678         Roo.each(this.radioes, function(i){
42679             
42680             if(i.value === v || i.value.toString() === v.toString()){
42681                 i.checked = true;
42682                 i.el.addClass('checked');
42683                 
42684                 if(suppressEvent !== true){
42685                     this.fireEvent('check', this, i);
42686                 }
42687                 
42688                 return false;
42689             }
42690             
42691         }, this);
42692         
42693         this.validate();
42694     },
42695     
42696     clearInvalid : function(){
42697         
42698         if(!this.el || this.preventMark){
42699             return;
42700         }
42701         
42702         this.el.removeClass([this.invalidClass]);
42703         
42704         this.fireEvent('valid', this);
42705     }
42706     
42707 });
42708
42709 Roo.apply(Roo.bootstrap.form.RadioSet, {
42710     
42711     groups: {},
42712     
42713     register : function(set)
42714     {
42715         this.groups[set.name] = set;
42716     },
42717     
42718     get: function(name) 
42719     {
42720         if (typeof(this.groups[name]) == 'undefined') {
42721             return false;
42722         }
42723         
42724         return this.groups[name] ;
42725     }
42726     
42727 });
42728 /*
42729  * Based on:
42730  * Ext JS Library 1.1.1
42731  * Copyright(c) 2006-2007, Ext JS, LLC.
42732  *
42733  * Originally Released Under LGPL - original licence link has changed is not relivant.
42734  *
42735  * Fork - LGPL
42736  * <script type="text/javascript">
42737  */
42738
42739
42740 /**
42741  * @class Roo.bootstrap.SplitBar
42742  * @extends Roo.util.Observable
42743  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42744  * <br><br>
42745  * Usage:
42746  * <pre><code>
42747 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42748                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42749 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42750 split.minSize = 100;
42751 split.maxSize = 600;
42752 split.animate = true;
42753 split.on('moved', splitterMoved);
42754 </code></pre>
42755  * @constructor
42756  * Create a new SplitBar
42757  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42758  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42759  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42760  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42761                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42762                         position of the SplitBar).
42763  */
42764 Roo.bootstrap.SplitBar = function(cfg){
42765     
42766     /** @private */
42767     
42768     //{
42769     //  dragElement : elm
42770     //  resizingElement: el,
42771         // optional..
42772     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42773     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42774         // existingProxy ???
42775     //}
42776     
42777     this.el = Roo.get(cfg.dragElement, true);
42778     this.el.dom.unselectable = "on";
42779     /** @private */
42780     this.resizingEl = Roo.get(cfg.resizingElement, true);
42781
42782     /**
42783      * @private
42784      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42785      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42786      * @type Number
42787      */
42788     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42789     
42790     /**
42791      * The minimum size of the resizing element. (Defaults to 0)
42792      * @type Number
42793      */
42794     this.minSize = 0;
42795     
42796     /**
42797      * The maximum size of the resizing element. (Defaults to 2000)
42798      * @type Number
42799      */
42800     this.maxSize = 2000;
42801     
42802     /**
42803      * Whether to animate the transition to the new size
42804      * @type Boolean
42805      */
42806     this.animate = false;
42807     
42808     /**
42809      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42810      * @type Boolean
42811      */
42812     this.useShim = false;
42813     
42814     /** @private */
42815     this.shim = null;
42816     
42817     if(!cfg.existingProxy){
42818         /** @private */
42819         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42820     }else{
42821         this.proxy = Roo.get(cfg.existingProxy).dom;
42822     }
42823     /** @private */
42824     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42825     
42826     /** @private */
42827     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42828     
42829     /** @private */
42830     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42831     
42832     /** @private */
42833     this.dragSpecs = {};
42834     
42835     /**
42836      * @private The adapter to use to positon and resize elements
42837      */
42838     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42839     this.adapter.init(this);
42840     
42841     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42842         /** @private */
42843         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42844         this.el.addClass("roo-splitbar-h");
42845     }else{
42846         /** @private */
42847         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42848         this.el.addClass("roo-splitbar-v");
42849     }
42850     
42851     this.addEvents({
42852         /**
42853          * @event resize
42854          * Fires when the splitter is moved (alias for {@link #event-moved})
42855          * @param {Roo.bootstrap.SplitBar} this
42856          * @param {Number} newSize the new width or height
42857          */
42858         "resize" : true,
42859         /**
42860          * @event moved
42861          * Fires when the splitter is moved
42862          * @param {Roo.bootstrap.SplitBar} this
42863          * @param {Number} newSize the new width or height
42864          */
42865         "moved" : true,
42866         /**
42867          * @event beforeresize
42868          * Fires before the splitter is dragged
42869          * @param {Roo.bootstrap.SplitBar} this
42870          */
42871         "beforeresize" : true,
42872
42873         "beforeapply" : true
42874     });
42875
42876     Roo.util.Observable.call(this);
42877 };
42878
42879 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42880     onStartProxyDrag : function(x, y){
42881         this.fireEvent("beforeresize", this);
42882         if(!this.overlay){
42883             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42884             o.unselectable();
42885             o.enableDisplayMode("block");
42886             // all splitbars share the same overlay
42887             Roo.bootstrap.SplitBar.prototype.overlay = o;
42888         }
42889         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42890         this.overlay.show();
42891         Roo.get(this.proxy).setDisplayed("block");
42892         var size = this.adapter.getElementSize(this);
42893         this.activeMinSize = this.getMinimumSize();;
42894         this.activeMaxSize = this.getMaximumSize();;
42895         var c1 = size - this.activeMinSize;
42896         var c2 = Math.max(this.activeMaxSize - size, 0);
42897         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42898             this.dd.resetConstraints();
42899             this.dd.setXConstraint(
42900                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42901                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42902             );
42903             this.dd.setYConstraint(0, 0);
42904         }else{
42905             this.dd.resetConstraints();
42906             this.dd.setXConstraint(0, 0);
42907             this.dd.setYConstraint(
42908                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42909                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42910             );
42911          }
42912         this.dragSpecs.startSize = size;
42913         this.dragSpecs.startPoint = [x, y];
42914         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42915     },
42916     
42917     /** 
42918      * @private Called after the drag operation by the DDProxy
42919      */
42920     onEndProxyDrag : function(e){
42921         Roo.get(this.proxy).setDisplayed(false);
42922         var endPoint = Roo.lib.Event.getXY(e);
42923         if(this.overlay){
42924             this.overlay.hide();
42925         }
42926         var newSize;
42927         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42928             newSize = this.dragSpecs.startSize + 
42929                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42930                     endPoint[0] - this.dragSpecs.startPoint[0] :
42931                     this.dragSpecs.startPoint[0] - endPoint[0]
42932                 );
42933         }else{
42934             newSize = this.dragSpecs.startSize + 
42935                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42936                     endPoint[1] - this.dragSpecs.startPoint[1] :
42937                     this.dragSpecs.startPoint[1] - endPoint[1]
42938                 );
42939         }
42940         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42941         if(newSize != this.dragSpecs.startSize){
42942             if(this.fireEvent('beforeapply', this, newSize) !== false){
42943                 this.adapter.setElementSize(this, newSize);
42944                 this.fireEvent("moved", this, newSize);
42945                 this.fireEvent("resize", this, newSize);
42946             }
42947         }
42948     },
42949     
42950     /**
42951      * Get the adapter this SplitBar uses
42952      * @return The adapter object
42953      */
42954     getAdapter : function(){
42955         return this.adapter;
42956     },
42957     
42958     /**
42959      * Set the adapter this SplitBar uses
42960      * @param {Object} adapter A SplitBar adapter object
42961      */
42962     setAdapter : function(adapter){
42963         this.adapter = adapter;
42964         this.adapter.init(this);
42965     },
42966     
42967     /**
42968      * Gets the minimum size for the resizing element
42969      * @return {Number} The minimum size
42970      */
42971     getMinimumSize : function(){
42972         return this.minSize;
42973     },
42974     
42975     /**
42976      * Sets the minimum size for the resizing element
42977      * @param {Number} minSize The minimum size
42978      */
42979     setMinimumSize : function(minSize){
42980         this.minSize = minSize;
42981     },
42982     
42983     /**
42984      * Gets the maximum size for the resizing element
42985      * @return {Number} The maximum size
42986      */
42987     getMaximumSize : function(){
42988         return this.maxSize;
42989     },
42990     
42991     /**
42992      * Sets the maximum size for the resizing element
42993      * @param {Number} maxSize The maximum size
42994      */
42995     setMaximumSize : function(maxSize){
42996         this.maxSize = maxSize;
42997     },
42998     
42999     /**
43000      * Sets the initialize size for the resizing element
43001      * @param {Number} size The initial size
43002      */
43003     setCurrentSize : function(size){
43004         var oldAnimate = this.animate;
43005         this.animate = false;
43006         this.adapter.setElementSize(this, size);
43007         this.animate = oldAnimate;
43008     },
43009     
43010     /**
43011      * Destroy this splitbar. 
43012      * @param {Boolean} removeEl True to remove the element
43013      */
43014     destroy : function(removeEl){
43015         if(this.shim){
43016             this.shim.remove();
43017         }
43018         this.dd.unreg();
43019         this.proxy.parentNode.removeChild(this.proxy);
43020         if(removeEl){
43021             this.el.remove();
43022         }
43023     }
43024 });
43025
43026 /**
43027  * @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.
43028  */
43029 Roo.bootstrap.SplitBar.createProxy = function(dir){
43030     var proxy = new Roo.Element(document.createElement("div"));
43031     proxy.unselectable();
43032     var cls = 'roo-splitbar-proxy';
43033     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
43034     document.body.appendChild(proxy.dom);
43035     return proxy.dom;
43036 };
43037
43038 /** 
43039  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
43040  * Default Adapter. It assumes the splitter and resizing element are not positioned
43041  * elements and only gets/sets the width of the element. Generally used for table based layouts.
43042  */
43043 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
43044 };
43045
43046 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
43047     // do nothing for now
43048     init : function(s){
43049     
43050     },
43051     /**
43052      * Called before drag operations to get the current size of the resizing element. 
43053      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
43054      */
43055      getElementSize : function(s){
43056         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
43057             return s.resizingEl.getWidth();
43058         }else{
43059             return s.resizingEl.getHeight();
43060         }
43061     },
43062     
43063     /**
43064      * Called after drag operations to set the size of the resizing element.
43065      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
43066      * @param {Number} newSize The new size to set
43067      * @param {Function} onComplete A function to be invoked when resizing is complete
43068      */
43069     setElementSize : function(s, newSize, onComplete){
43070         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
43071             if(!s.animate){
43072                 s.resizingEl.setWidth(newSize);
43073                 if(onComplete){
43074                     onComplete(s, newSize);
43075                 }
43076             }else{
43077                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
43078             }
43079         }else{
43080             
43081             if(!s.animate){
43082                 s.resizingEl.setHeight(newSize);
43083                 if(onComplete){
43084                     onComplete(s, newSize);
43085                 }
43086             }else{
43087                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
43088             }
43089         }
43090     }
43091 };
43092
43093 /** 
43094  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
43095  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
43096  * Adapter that  moves the splitter element to align with the resized sizing element. 
43097  * Used with an absolute positioned SplitBar.
43098  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
43099  * document.body, make sure you assign an id to the body element.
43100  */
43101 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
43102     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
43103     this.container = Roo.get(container);
43104 };
43105
43106 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
43107     init : function(s){
43108         this.basic.init(s);
43109     },
43110     
43111     getElementSize : function(s){
43112         return this.basic.getElementSize(s);
43113     },
43114     
43115     setElementSize : function(s, newSize, onComplete){
43116         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
43117     },
43118     
43119     moveSplitter : function(s){
43120         var yes = Roo.bootstrap.SplitBar;
43121         switch(s.placement){
43122             case yes.LEFT:
43123                 s.el.setX(s.resizingEl.getRight());
43124                 break;
43125             case yes.RIGHT:
43126                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
43127                 break;
43128             case yes.TOP:
43129                 s.el.setY(s.resizingEl.getBottom());
43130                 break;
43131             case yes.BOTTOM:
43132                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
43133                 break;
43134         }
43135     }
43136 };
43137
43138 /**
43139  * Orientation constant - Create a vertical SplitBar
43140  * @static
43141  * @type Number
43142  */
43143 Roo.bootstrap.SplitBar.VERTICAL = 1;
43144
43145 /**
43146  * Orientation constant - Create a horizontal SplitBar
43147  * @static
43148  * @type Number
43149  */
43150 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
43151
43152 /**
43153  * Placement constant - The resizing element is to the left of the splitter element
43154  * @static
43155  * @type Number
43156  */
43157 Roo.bootstrap.SplitBar.LEFT = 1;
43158
43159 /**
43160  * Placement constant - The resizing element is to the right of the splitter element
43161  * @static
43162  * @type Number
43163  */
43164 Roo.bootstrap.SplitBar.RIGHT = 2;
43165
43166 /**
43167  * Placement constant - The resizing element is positioned above the splitter element
43168  * @static
43169  * @type Number
43170  */
43171 Roo.bootstrap.SplitBar.TOP = 3;
43172
43173 /**
43174  * Placement constant - The resizing element is positioned under splitter element
43175  * @static
43176  * @type Number
43177  */
43178 Roo.bootstrap.SplitBar.BOTTOM = 4;
43179 /*
43180  * Based on:
43181  * Ext JS Library 1.1.1
43182  * Copyright(c) 2006-2007, Ext JS, LLC.
43183  *
43184  * Originally Released Under LGPL - original licence link has changed is not relivant.
43185  *
43186  * Fork - LGPL
43187  * <script type="text/javascript">
43188  */
43189
43190 /**
43191  * @class Roo.bootstrap.layout.Manager
43192  * @extends Roo.bootstrap.Component
43193  * @abstract
43194  * Base class for layout managers.
43195  */
43196 Roo.bootstrap.layout.Manager = function(config)
43197 {
43198     this.monitorWindowResize = true; // do this before we apply configuration.
43199     
43200     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
43201
43202
43203
43204
43205
43206     /** false to disable window resize monitoring @type Boolean */
43207     
43208     this.regions = {};
43209     this.addEvents({
43210         /**
43211          * @event layout
43212          * Fires when a layout is performed.
43213          * @param {Roo.LayoutManager} this
43214          */
43215         "layout" : true,
43216         /**
43217          * @event regionresized
43218          * Fires when the user resizes a region.
43219          * @param {Roo.LayoutRegion} region The resized region
43220          * @param {Number} newSize The new size (width for east/west, height for north/south)
43221          */
43222         "regionresized" : true,
43223         /**
43224          * @event regioncollapsed
43225          * Fires when a region is collapsed.
43226          * @param {Roo.LayoutRegion} region The collapsed region
43227          */
43228         "regioncollapsed" : true,
43229         /**
43230          * @event regionexpanded
43231          * Fires when a region is expanded.
43232          * @param {Roo.LayoutRegion} region The expanded region
43233          */
43234         "regionexpanded" : true
43235     });
43236     this.updating = false;
43237
43238     if (config.el) {
43239         this.el = Roo.get(config.el);
43240         this.initEvents();
43241     }
43242
43243 };
43244
43245 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
43246
43247
43248     regions : null,
43249
43250     monitorWindowResize : true,
43251
43252
43253     updating : false,
43254
43255
43256     onRender : function(ct, position)
43257     {
43258         if(!this.el){
43259             this.el = Roo.get(ct);
43260             this.initEvents();
43261         }
43262         //this.fireEvent('render',this);
43263     },
43264
43265
43266     initEvents: function()
43267     {
43268
43269
43270         // ie scrollbar fix
43271         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43272             document.body.scroll = "no";
43273         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43274             this.el.position('relative');
43275         }
43276         this.id = this.el.id;
43277         this.el.addClass("roo-layout-container");
43278         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43279         if(this.el.dom != document.body ) {
43280             this.el.on('resize', this.layout,this);
43281             this.el.on('show', this.layout,this);
43282         }
43283
43284     },
43285
43286     /**
43287      * Returns true if this layout is currently being updated
43288      * @return {Boolean}
43289      */
43290     isUpdating : function(){
43291         return this.updating;
43292     },
43293
43294     /**
43295      * Suspend the LayoutManager from doing auto-layouts while
43296      * making multiple add or remove calls
43297      */
43298     beginUpdate : function(){
43299         this.updating = true;
43300     },
43301
43302     /**
43303      * Restore auto-layouts and optionally disable the manager from performing a layout
43304      * @param {Boolean} noLayout true to disable a layout update
43305      */
43306     endUpdate : function(noLayout){
43307         this.updating = false;
43308         if(!noLayout){
43309             this.layout();
43310         }
43311     },
43312
43313     layout: function(){
43314         // abstract...
43315     },
43316
43317     onRegionResized : function(region, newSize){
43318         this.fireEvent("regionresized", region, newSize);
43319         this.layout();
43320     },
43321
43322     onRegionCollapsed : function(region){
43323         this.fireEvent("regioncollapsed", region);
43324     },
43325
43326     onRegionExpanded : function(region){
43327         this.fireEvent("regionexpanded", region);
43328     },
43329
43330     /**
43331      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43332      * performs box-model adjustments.
43333      * @return {Object} The size as an object {width: (the width), height: (the height)}
43334      */
43335     getViewSize : function()
43336     {
43337         var size;
43338         if(this.el.dom != document.body){
43339             size = this.el.getSize();
43340         }else{
43341             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43342         }
43343         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43344         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43345         return size;
43346     },
43347
43348     /**
43349      * Returns the Element this layout is bound to.
43350      * @return {Roo.Element}
43351      */
43352     getEl : function(){
43353         return this.el;
43354     },
43355
43356     /**
43357      * Returns the specified region.
43358      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43359      * @return {Roo.LayoutRegion}
43360      */
43361     getRegion : function(target){
43362         return this.regions[target.toLowerCase()];
43363     },
43364
43365     onWindowResize : function(){
43366         if(this.monitorWindowResize){
43367             this.layout();
43368         }
43369     }
43370 });
43371 /*
43372  * Based on:
43373  * Ext JS Library 1.1.1
43374  * Copyright(c) 2006-2007, Ext JS, LLC.
43375  *
43376  * Originally Released Under LGPL - original licence link has changed is not relivant.
43377  *
43378  * Fork - LGPL
43379  * <script type="text/javascript">
43380  */
43381 /**
43382  * @class Roo.bootstrap.layout.Border
43383  * @extends Roo.bootstrap.layout.Manager
43384  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43385  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43386  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43387  * please see: examples/bootstrap/nested.html<br><br>
43388  
43389 <b>The container the layout is rendered into can be either the body element or any other element.
43390 If it is not the body element, the container needs to either be an absolute positioned element,
43391 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43392 the container size if it is not the body element.</b>
43393
43394 * @constructor
43395 * Create a new Border
43396 * @param {Object} config Configuration options
43397  */
43398 Roo.bootstrap.layout.Border = function(config){
43399     config = config || {};
43400     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43401     
43402     
43403     
43404     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43405         if(config[region]){
43406             config[region].region = region;
43407             this.addRegion(config[region]);
43408         }
43409     },this);
43410     
43411 };
43412
43413 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
43414
43415 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43416     
43417         /**
43418          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43419          */
43420         /**
43421          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43422          */
43423         /**
43424          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43425          */
43426         /**
43427          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43428          */
43429         /**
43430          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43431          */
43432         
43433         
43434         
43435         
43436     parent : false, // this might point to a 'nest' or a ???
43437     
43438     /**
43439      * Creates and adds a new region if it doesn't already exist.
43440      * @param {String} target The target region key (north, south, east, west or center).
43441      * @param {Object} config The regions config object
43442      * @return {BorderLayoutRegion} The new region
43443      */
43444     addRegion : function(config)
43445     {
43446         if(!this.regions[config.region]){
43447             var r = this.factory(config);
43448             this.bindRegion(r);
43449         }
43450         return this.regions[config.region];
43451     },
43452
43453     // private (kinda)
43454     bindRegion : function(r){
43455         this.regions[r.config.region] = r;
43456         
43457         r.on("visibilitychange",    this.layout, this);
43458         r.on("paneladded",          this.layout, this);
43459         r.on("panelremoved",        this.layout, this);
43460         r.on("invalidated",         this.layout, this);
43461         r.on("resized",             this.onRegionResized, this);
43462         r.on("collapsed",           this.onRegionCollapsed, this);
43463         r.on("expanded",            this.onRegionExpanded, this);
43464     },
43465
43466     /**
43467      * Performs a layout update.
43468      */
43469     layout : function()
43470     {
43471         if(this.updating) {
43472             return;
43473         }
43474         
43475         // render all the rebions if they have not been done alreayd?
43476         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43477             if(this.regions[region] && !this.regions[region].bodyEl){
43478                 this.regions[region].onRender(this.el)
43479             }
43480         },this);
43481         
43482         var size = this.getViewSize();
43483         var w = size.width;
43484         var h = size.height;
43485         var centerW = w;
43486         var centerH = h;
43487         var centerY = 0;
43488         var centerX = 0;
43489         //var x = 0, y = 0;
43490
43491         var rs = this.regions;
43492         var north = rs["north"];
43493         var south = rs["south"]; 
43494         var west = rs["west"];
43495         var east = rs["east"];
43496         var center = rs["center"];
43497         //if(this.hideOnLayout){ // not supported anymore
43498             //c.el.setStyle("display", "none");
43499         //}
43500         if(north && north.isVisible()){
43501             var b = north.getBox();
43502             var m = north.getMargins();
43503             b.width = w - (m.left+m.right);
43504             b.x = m.left;
43505             b.y = m.top;
43506             centerY = b.height + b.y + m.bottom;
43507             centerH -= centerY;
43508             north.updateBox(this.safeBox(b));
43509         }
43510         if(south && south.isVisible()){
43511             var b = south.getBox();
43512             var m = south.getMargins();
43513             b.width = w - (m.left+m.right);
43514             b.x = m.left;
43515             var totalHeight = (b.height + m.top + m.bottom);
43516             b.y = h - totalHeight + m.top;
43517             centerH -= totalHeight;
43518             south.updateBox(this.safeBox(b));
43519         }
43520         if(west && west.isVisible()){
43521             var b = west.getBox();
43522             var m = west.getMargins();
43523             b.height = centerH - (m.top+m.bottom);
43524             b.x = m.left;
43525             b.y = centerY + m.top;
43526             var totalWidth = (b.width + m.left + m.right);
43527             centerX += totalWidth;
43528             centerW -= totalWidth;
43529             west.updateBox(this.safeBox(b));
43530         }
43531         if(east && east.isVisible()){
43532             var b = east.getBox();
43533             var m = east.getMargins();
43534             b.height = centerH - (m.top+m.bottom);
43535             var totalWidth = (b.width + m.left + m.right);
43536             b.x = w - totalWidth + m.left;
43537             b.y = centerY + m.top;
43538             centerW -= totalWidth;
43539             east.updateBox(this.safeBox(b));
43540         }
43541         if(center){
43542             var m = center.getMargins();
43543             var centerBox = {
43544                 x: centerX + m.left,
43545                 y: centerY + m.top,
43546                 width: centerW - (m.left+m.right),
43547                 height: centerH - (m.top+m.bottom)
43548             };
43549             //if(this.hideOnLayout){
43550                 //center.el.setStyle("display", "block");
43551             //}
43552             center.updateBox(this.safeBox(centerBox));
43553         }
43554         this.el.repaint();
43555         this.fireEvent("layout", this);
43556     },
43557
43558     // private
43559     safeBox : function(box){
43560         box.width = Math.max(0, box.width);
43561         box.height = Math.max(0, box.height);
43562         return box;
43563     },
43564
43565     /**
43566      * Adds a ContentPanel (or subclass) to this layout.
43567      * @param {String} target The target region key (north, south, east, west or center).
43568      * @param {Roo.ContentPanel} panel The panel to add
43569      * @return {Roo.ContentPanel} The added panel
43570      */
43571     add : function(target, panel){
43572          
43573         target = target.toLowerCase();
43574         return this.regions[target].add(panel);
43575     },
43576
43577     /**
43578      * Remove a ContentPanel (or subclass) to this layout.
43579      * @param {String} target The target region key (north, south, east, west or center).
43580      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43581      * @return {Roo.ContentPanel} The removed panel
43582      */
43583     remove : function(target, panel){
43584         target = target.toLowerCase();
43585         return this.regions[target].remove(panel);
43586     },
43587
43588     /**
43589      * Searches all regions for a panel with the specified id
43590      * @param {String} panelId
43591      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43592      */
43593     findPanel : function(panelId){
43594         var rs = this.regions;
43595         for(var target in rs){
43596             if(typeof rs[target] != "function"){
43597                 var p = rs[target].getPanel(panelId);
43598                 if(p){
43599                     return p;
43600                 }
43601             }
43602         }
43603         return null;
43604     },
43605
43606     /**
43607      * Searches all regions for a panel with the specified id and activates (shows) it.
43608      * @param {String/ContentPanel} panelId The panels id or the panel itself
43609      * @return {Roo.ContentPanel} The shown panel or null
43610      */
43611     showPanel : function(panelId) {
43612       var rs = this.regions;
43613       for(var target in rs){
43614          var r = rs[target];
43615          if(typeof r != "function"){
43616             if(r.hasPanel(panelId)){
43617                return r.showPanel(panelId);
43618             }
43619          }
43620       }
43621       return null;
43622    },
43623
43624    /**
43625      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43626      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43627      */
43628    /*
43629     restoreState : function(provider){
43630         if(!provider){
43631             provider = Roo.state.Manager;
43632         }
43633         var sm = new Roo.LayoutStateManager();
43634         sm.init(this, provider);
43635     },
43636 */
43637  
43638  
43639     /**
43640      * Adds a xtype elements to the layout.
43641      * <pre><code>
43642
43643 layout.addxtype({
43644        xtype : 'ContentPanel',
43645        region: 'west',
43646        items: [ .... ]
43647    }
43648 );
43649
43650 layout.addxtype({
43651         xtype : 'NestedLayoutPanel',
43652         region: 'west',
43653         layout: {
43654            center: { },
43655            west: { }   
43656         },
43657         items : [ ... list of content panels or nested layout panels.. ]
43658    }
43659 );
43660 </code></pre>
43661      * @param {Object} cfg Xtype definition of item to add.
43662      */
43663     addxtype : function(cfg)
43664     {
43665         // basically accepts a pannel...
43666         // can accept a layout region..!?!?
43667         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43668         
43669         
43670         // theory?  children can only be panels??
43671         
43672         //if (!cfg.xtype.match(/Panel$/)) {
43673         //    return false;
43674         //}
43675         var ret = false;
43676         
43677         if (typeof(cfg.region) == 'undefined') {
43678             Roo.log("Failed to add Panel, region was not set");
43679             Roo.log(cfg);
43680             return false;
43681         }
43682         var region = cfg.region;
43683         delete cfg.region;
43684         
43685           
43686         var xitems = [];
43687         if (cfg.items) {
43688             xitems = cfg.items;
43689             delete cfg.items;
43690         }
43691         var nb = false;
43692         
43693         if ( region == 'center') {
43694             Roo.log("Center: " + cfg.title);
43695         }
43696         
43697         
43698         switch(cfg.xtype) 
43699         {
43700             case 'Content':  // ContentPanel (el, cfg)
43701             case 'Scroll':  // ContentPanel (el, cfg)
43702             case 'View': 
43703                 cfg.autoCreate = cfg.autoCreate || true;
43704                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43705                 //} else {
43706                 //    var el = this.el.createChild();
43707                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43708                 //}
43709                 
43710                 this.add(region, ret);
43711                 break;
43712             
43713             /*
43714             case 'TreePanel': // our new panel!
43715                 cfg.el = this.el.createChild();
43716                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43717                 this.add(region, ret);
43718                 break;
43719             */
43720             
43721             case 'Nest': 
43722                 // create a new Layout (which is  a Border Layout...
43723                 
43724                 var clayout = cfg.layout;
43725                 clayout.el  = this.el.createChild();
43726                 clayout.items   = clayout.items  || [];
43727                 
43728                 delete cfg.layout;
43729                 
43730                 // replace this exitems with the clayout ones..
43731                 xitems = clayout.items;
43732                  
43733                 // force background off if it's in center...
43734                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43735                     cfg.background = false;
43736                 }
43737                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43738                 
43739                 
43740                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43741                 //console.log('adding nested layout panel '  + cfg.toSource());
43742                 this.add(region, ret);
43743                 nb = {}; /// find first...
43744                 break;
43745             
43746             case 'Grid':
43747                 
43748                 // needs grid and region
43749                 
43750                 //var el = this.getRegion(region).el.createChild();
43751                 /*
43752                  *var el = this.el.createChild();
43753                 // create the grid first...
43754                 cfg.grid.container = el;
43755                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43756                 */
43757                 
43758                 if (region == 'center' && this.active ) {
43759                     cfg.background = false;
43760                 }
43761                 
43762                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43763                 
43764                 this.add(region, ret);
43765                 /*
43766                 if (cfg.background) {
43767                     // render grid on panel activation (if panel background)
43768                     ret.on('activate', function(gp) {
43769                         if (!gp.grid.rendered) {
43770                     //        gp.grid.render(el);
43771                         }
43772                     });
43773                 } else {
43774                   //  cfg.grid.render(el);
43775                 }
43776                 */
43777                 break;
43778            
43779            
43780             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43781                 // it was the old xcomponent building that caused this before.
43782                 // espeically if border is the top element in the tree.
43783                 ret = this;
43784                 break; 
43785                 
43786                     
43787                 
43788                 
43789                 
43790             default:
43791                 /*
43792                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43793                     
43794                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43795                     this.add(region, ret);
43796                 } else {
43797                 */
43798                     Roo.log(cfg);
43799                     throw "Can not add '" + cfg.xtype + "' to Border";
43800                     return null;
43801              
43802                                 
43803              
43804         }
43805         this.beginUpdate();
43806         // add children..
43807         var region = '';
43808         var abn = {};
43809         Roo.each(xitems, function(i)  {
43810             region = nb && i.region ? i.region : false;
43811             
43812             var add = ret.addxtype(i);
43813            
43814             if (region) {
43815                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43816                 if (!i.background) {
43817                     abn[region] = nb[region] ;
43818                 }
43819             }
43820             
43821         });
43822         this.endUpdate();
43823
43824         // make the last non-background panel active..
43825         //if (nb) { Roo.log(abn); }
43826         if (nb) {
43827             
43828             for(var r in abn) {
43829                 region = this.getRegion(r);
43830                 if (region) {
43831                     // tried using nb[r], but it does not work..
43832                      
43833                     region.showPanel(abn[r]);
43834                    
43835                 }
43836             }
43837         }
43838         return ret;
43839         
43840     },
43841     
43842     
43843 // private
43844     factory : function(cfg)
43845     {
43846         
43847         var validRegions = Roo.bootstrap.layout.Border.regions;
43848
43849         var target = cfg.region;
43850         cfg.mgr = this;
43851         
43852         var r = Roo.bootstrap.layout;
43853         Roo.log(target);
43854         switch(target){
43855             case "north":
43856                 return new r.North(cfg);
43857             case "south":
43858                 return new r.South(cfg);
43859             case "east":
43860                 return new r.East(cfg);
43861             case "west":
43862                 return new r.West(cfg);
43863             case "center":
43864                 return new r.Center(cfg);
43865         }
43866         throw 'Layout region "'+target+'" not supported.';
43867     }
43868     
43869     
43870 });
43871  /*
43872  * Based on:
43873  * Ext JS Library 1.1.1
43874  * Copyright(c) 2006-2007, Ext JS, LLC.
43875  *
43876  * Originally Released Under LGPL - original licence link has changed is not relivant.
43877  *
43878  * Fork - LGPL
43879  * <script type="text/javascript">
43880  */
43881  
43882 /**
43883  * @class Roo.bootstrap.layout.Basic
43884  * @extends Roo.util.Observable
43885  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43886  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43887  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43888  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43889  * @cfg {string}   region  the region that it inhabits..
43890  * @cfg {bool}   skipConfig skip config?
43891  * 
43892
43893  */
43894 Roo.bootstrap.layout.Basic = function(config){
43895     
43896     this.mgr = config.mgr;
43897     
43898     this.position = config.region;
43899     
43900     var skipConfig = config.skipConfig;
43901     
43902     this.events = {
43903         /**
43904          * @scope Roo.BasicLayoutRegion
43905          */
43906         
43907         /**
43908          * @event beforeremove
43909          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43910          * @param {Roo.LayoutRegion} this
43911          * @param {Roo.ContentPanel} panel The panel
43912          * @param {Object} e The cancel event object
43913          */
43914         "beforeremove" : true,
43915         /**
43916          * @event invalidated
43917          * Fires when the layout for this region is changed.
43918          * @param {Roo.LayoutRegion} this
43919          */
43920         "invalidated" : true,
43921         /**
43922          * @event visibilitychange
43923          * Fires when this region is shown or hidden 
43924          * @param {Roo.LayoutRegion} this
43925          * @param {Boolean} visibility true or false
43926          */
43927         "visibilitychange" : true,
43928         /**
43929          * @event paneladded
43930          * Fires when a panel is added. 
43931          * @param {Roo.LayoutRegion} this
43932          * @param {Roo.ContentPanel} panel The panel
43933          */
43934         "paneladded" : true,
43935         /**
43936          * @event panelremoved
43937          * Fires when a panel is removed. 
43938          * @param {Roo.LayoutRegion} this
43939          * @param {Roo.ContentPanel} panel The panel
43940          */
43941         "panelremoved" : true,
43942         /**
43943          * @event beforecollapse
43944          * Fires when this region before collapse.
43945          * @param {Roo.LayoutRegion} this
43946          */
43947         "beforecollapse" : true,
43948         /**
43949          * @event collapsed
43950          * Fires when this region is collapsed.
43951          * @param {Roo.LayoutRegion} this
43952          */
43953         "collapsed" : true,
43954         /**
43955          * @event expanded
43956          * Fires when this region is expanded.
43957          * @param {Roo.LayoutRegion} this
43958          */
43959         "expanded" : true,
43960         /**
43961          * @event slideshow
43962          * Fires when this region is slid into view.
43963          * @param {Roo.LayoutRegion} this
43964          */
43965         "slideshow" : true,
43966         /**
43967          * @event slidehide
43968          * Fires when this region slides out of view. 
43969          * @param {Roo.LayoutRegion} this
43970          */
43971         "slidehide" : true,
43972         /**
43973          * @event panelactivated
43974          * Fires when a panel is activated. 
43975          * @param {Roo.LayoutRegion} this
43976          * @param {Roo.ContentPanel} panel The activated panel
43977          */
43978         "panelactivated" : true,
43979         /**
43980          * @event resized
43981          * Fires when the user resizes this region. 
43982          * @param {Roo.LayoutRegion} this
43983          * @param {Number} newSize The new size (width for east/west, height for north/south)
43984          */
43985         "resized" : true
43986     };
43987     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43988     this.panels = new Roo.util.MixedCollection();
43989     this.panels.getKey = this.getPanelId.createDelegate(this);
43990     this.box = null;
43991     this.activePanel = null;
43992     // ensure listeners are added...
43993     
43994     if (config.listeners || config.events) {
43995         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43996             listeners : config.listeners || {},
43997             events : config.events || {}
43998         });
43999     }
44000     
44001     if(skipConfig !== true){
44002         this.applyConfig(config);
44003     }
44004 };
44005
44006 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
44007 {
44008     getPanelId : function(p){
44009         return p.getId();
44010     },
44011     
44012     applyConfig : function(config){
44013         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44014         this.config = config;
44015         
44016     },
44017     
44018     /**
44019      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44020      * the width, for horizontal (north, south) the height.
44021      * @param {Number} newSize The new width or height
44022      */
44023     resizeTo : function(newSize){
44024         var el = this.el ? this.el :
44025                  (this.activePanel ? this.activePanel.getEl() : null);
44026         if(el){
44027             switch(this.position){
44028                 case "east":
44029                 case "west":
44030                     el.setWidth(newSize);
44031                     this.fireEvent("resized", this, newSize);
44032                 break;
44033                 case "north":
44034                 case "south":
44035                     el.setHeight(newSize);
44036                     this.fireEvent("resized", this, newSize);
44037                 break;                
44038             }
44039         }
44040     },
44041     
44042     getBox : function(){
44043         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44044     },
44045     
44046     getMargins : function(){
44047         return this.margins;
44048     },
44049     
44050     updateBox : function(box){
44051         this.box = box;
44052         var el = this.activePanel.getEl();
44053         el.dom.style.left = box.x + "px";
44054         el.dom.style.top = box.y + "px";
44055         this.activePanel.setSize(box.width, box.height);
44056     },
44057     
44058     /**
44059      * Returns the container element for this region.
44060      * @return {Roo.Element}
44061      */
44062     getEl : function(){
44063         return this.activePanel;
44064     },
44065     
44066     /**
44067      * Returns true if this region is currently visible.
44068      * @return {Boolean}
44069      */
44070     isVisible : function(){
44071         return this.activePanel ? true : false;
44072     },
44073     
44074     setActivePanel : function(panel){
44075         panel = this.getPanel(panel);
44076         if(this.activePanel && this.activePanel != panel){
44077             this.activePanel.setActiveState(false);
44078             this.activePanel.getEl().setLeftTop(-10000,-10000);
44079         }
44080         this.activePanel = panel;
44081         panel.setActiveState(true);
44082         if(this.box){
44083             panel.setSize(this.box.width, this.box.height);
44084         }
44085         this.fireEvent("panelactivated", this, panel);
44086         this.fireEvent("invalidated");
44087     },
44088     
44089     /**
44090      * Show the specified panel.
44091      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44092      * @return {Roo.ContentPanel} The shown panel or null
44093      */
44094     showPanel : function(panel){
44095         panel = this.getPanel(panel);
44096         if(panel){
44097             this.setActivePanel(panel);
44098         }
44099         return panel;
44100     },
44101     
44102     /**
44103      * Get the active panel for this region.
44104      * @return {Roo.ContentPanel} The active panel or null
44105      */
44106     getActivePanel : function(){
44107         return this.activePanel;
44108     },
44109     
44110     /**
44111      * Add the passed ContentPanel(s)
44112      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44113      * @return {Roo.ContentPanel} The panel added (if only one was added)
44114      */
44115     add : function(panel){
44116         if(arguments.length > 1){
44117             for(var i = 0, len = arguments.length; i < len; i++) {
44118                 this.add(arguments[i]);
44119             }
44120             return null;
44121         }
44122         if(this.hasPanel(panel)){
44123             this.showPanel(panel);
44124             return panel;
44125         }
44126         var el = panel.getEl();
44127         if(el.dom.parentNode != this.mgr.el.dom){
44128             this.mgr.el.dom.appendChild(el.dom);
44129         }
44130         if(panel.setRegion){
44131             panel.setRegion(this);
44132         }
44133         this.panels.add(panel);
44134         el.setStyle("position", "absolute");
44135         if(!panel.background){
44136             this.setActivePanel(panel);
44137             if(this.config.initialSize && this.panels.getCount()==1){
44138                 this.resizeTo(this.config.initialSize);
44139             }
44140         }
44141         this.fireEvent("paneladded", this, panel);
44142         return panel;
44143     },
44144     
44145     /**
44146      * Returns true if the panel is in this region.
44147      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44148      * @return {Boolean}
44149      */
44150     hasPanel : function(panel){
44151         if(typeof panel == "object"){ // must be panel obj
44152             panel = panel.getId();
44153         }
44154         return this.getPanel(panel) ? true : false;
44155     },
44156     
44157     /**
44158      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44159      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44160      * @param {Boolean} preservePanel Overrides the config preservePanel option
44161      * @return {Roo.ContentPanel} The panel that was removed
44162      */
44163     remove : function(panel, preservePanel){
44164         panel = this.getPanel(panel);
44165         if(!panel){
44166             return null;
44167         }
44168         var e = {};
44169         this.fireEvent("beforeremove", this, panel, e);
44170         if(e.cancel === true){
44171             return null;
44172         }
44173         var panelId = panel.getId();
44174         this.panels.removeKey(panelId);
44175         return panel;
44176     },
44177     
44178     /**
44179      * Returns the panel specified or null if it's not in this region.
44180      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44181      * @return {Roo.ContentPanel}
44182      */
44183     getPanel : function(id){
44184         if(typeof id == "object"){ // must be panel obj
44185             return id;
44186         }
44187         return this.panels.get(id);
44188     },
44189     
44190     /**
44191      * Returns this regions position (north/south/east/west/center).
44192      * @return {String} 
44193      */
44194     getPosition: function(){
44195         return this.position;    
44196     }
44197 });/*
44198  * Based on:
44199  * Ext JS Library 1.1.1
44200  * Copyright(c) 2006-2007, Ext JS, LLC.
44201  *
44202  * Originally Released Under LGPL - original licence link has changed is not relivant.
44203  *
44204  * Fork - LGPL
44205  * <script type="text/javascript">
44206  */
44207  
44208 /**
44209  * @class Roo.bootstrap.layout.Region
44210  * @extends Roo.bootstrap.layout.Basic
44211  * This class represents a region in a layout manager.
44212  
44213  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44214  * @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})
44215  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
44216  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
44217  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
44218  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
44219  * @cfg {String}    title           The title for the region (overrides panel titles)
44220  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
44221  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44222  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
44223  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44224  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
44225  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44226  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
44227  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
44228  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
44229  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
44230
44231  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
44232  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
44233  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
44234  * @cfg {Number}    width           For East/West panels
44235  * @cfg {Number}    height          For North/South panels
44236  * @cfg {Boolean}   split           To show the splitter
44237  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
44238  * 
44239  * @cfg {string}   cls             Extra CSS classes to add to region
44240  * 
44241  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
44242  * @cfg {string}   region  the region that it inhabits..
44243  *
44244
44245  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
44246  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
44247
44248  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
44249  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
44250  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
44251  */
44252 Roo.bootstrap.layout.Region = function(config)
44253 {
44254     this.applyConfig(config);
44255
44256     var mgr = config.mgr;
44257     var pos = config.region;
44258     config.skipConfig = true;
44259     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
44260     
44261     if (mgr.el) {
44262         this.onRender(mgr.el);   
44263     }
44264      
44265     this.visible = true;
44266     this.collapsed = false;
44267     this.unrendered_panels = [];
44268 };
44269
44270 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
44271
44272     position: '', // set by wrapper (eg. north/south etc..)
44273     unrendered_panels : null,  // unrendered panels.
44274     
44275     tabPosition : false,
44276     
44277     mgr: false, // points to 'Border'
44278     
44279     
44280     createBody : function(){
44281         /** This region's body element 
44282         * @type Roo.Element */
44283         this.bodyEl = this.el.createChild({
44284                 tag: "div",
44285                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
44286         });
44287     },
44288
44289     onRender: function(ctr, pos)
44290     {
44291         var dh = Roo.DomHelper;
44292         /** This region's container element 
44293         * @type Roo.Element */
44294         this.el = dh.append(ctr.dom, {
44295                 tag: "div",
44296                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
44297             }, true);
44298         /** This region's title element 
44299         * @type Roo.Element */
44300     
44301         this.titleEl = dh.append(this.el.dom,  {
44302                 tag: "div",
44303                 unselectable: "on",
44304                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
44305                 children:[
44306                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44307                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
44308                 ]
44309             }, true);
44310         
44311         this.titleEl.enableDisplayMode();
44312         /** This region's title text element 
44313         * @type HTMLElement */
44314         this.titleTextEl = this.titleEl.dom.firstChild;
44315         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44316         /*
44317         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
44318         this.closeBtn.enableDisplayMode();
44319         this.closeBtn.on("click", this.closeClicked, this);
44320         this.closeBtn.hide();
44321     */
44322         this.createBody(this.config);
44323         if(this.config.hideWhenEmpty){
44324             this.hide();
44325             this.on("paneladded", this.validateVisibility, this);
44326             this.on("panelremoved", this.validateVisibility, this);
44327         }
44328         if(this.autoScroll){
44329             this.bodyEl.setStyle("overflow", "auto");
44330         }else{
44331             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44332         }
44333         //if(c.titlebar !== false){
44334             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44335                 this.titleEl.hide();
44336             }else{
44337                 this.titleEl.show();
44338                 if(this.config.title){
44339                     this.titleTextEl.innerHTML = this.config.title;
44340                 }
44341             }
44342         //}
44343         if(this.config.collapsed){
44344             this.collapse(true);
44345         }
44346         if(this.config.hidden){
44347             this.hide();
44348         }
44349         
44350         if (this.unrendered_panels && this.unrendered_panels.length) {
44351             for (var i =0;i< this.unrendered_panels.length; i++) {
44352                 this.add(this.unrendered_panels[i]);
44353             }
44354             this.unrendered_panels = null;
44355             
44356         }
44357         
44358     },
44359     
44360     applyConfig : function(c)
44361     {
44362         /*
44363          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44364             var dh = Roo.DomHelper;
44365             if(c.titlebar !== false){
44366                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44367                 this.collapseBtn.on("click", this.collapse, this);
44368                 this.collapseBtn.enableDisplayMode();
44369                 /*
44370                 if(c.showPin === true || this.showPin){
44371                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44372                     this.stickBtn.enableDisplayMode();
44373                     this.stickBtn.on("click", this.expand, this);
44374                     this.stickBtn.hide();
44375                 }
44376                 
44377             }
44378             */
44379             /** This region's collapsed element
44380             * @type Roo.Element */
44381             /*
44382              *
44383             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44384                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44385             ]}, true);
44386             
44387             if(c.floatable !== false){
44388                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44389                this.collapsedEl.on("click", this.collapseClick, this);
44390             }
44391
44392             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44393                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44394                    id: "message", unselectable: "on", style:{"float":"left"}});
44395                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44396              }
44397             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44398             this.expandBtn.on("click", this.expand, this);
44399             
44400         }
44401         
44402         if(this.collapseBtn){
44403             this.collapseBtn.setVisible(c.collapsible == true);
44404         }
44405         
44406         this.cmargins = c.cmargins || this.cmargins ||
44407                          (this.position == "west" || this.position == "east" ?
44408                              {top: 0, left: 2, right:2, bottom: 0} :
44409                              {top: 2, left: 0, right:0, bottom: 2});
44410         */
44411         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44412         
44413         
44414         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44415         
44416         this.autoScroll = c.autoScroll || false;
44417         
44418         
44419        
44420         
44421         this.duration = c.duration || .30;
44422         this.slideDuration = c.slideDuration || .45;
44423         this.config = c;
44424        
44425     },
44426     /**
44427      * Returns true if this region is currently visible.
44428      * @return {Boolean}
44429      */
44430     isVisible : function(){
44431         return this.visible;
44432     },
44433
44434     /**
44435      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44436      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44437      */
44438     //setCollapsedTitle : function(title){
44439     //    title = title || "&#160;";
44440      //   if(this.collapsedTitleTextEl){
44441       //      this.collapsedTitleTextEl.innerHTML = title;
44442        // }
44443     //},
44444
44445     getBox : function(){
44446         var b;
44447       //  if(!this.collapsed){
44448             b = this.el.getBox(false, true);
44449        // }else{
44450           //  b = this.collapsedEl.getBox(false, true);
44451         //}
44452         return b;
44453     },
44454
44455     getMargins : function(){
44456         return this.margins;
44457         //return this.collapsed ? this.cmargins : this.margins;
44458     },
44459 /*
44460     highlight : function(){
44461         this.el.addClass("x-layout-panel-dragover");
44462     },
44463
44464     unhighlight : function(){
44465         this.el.removeClass("x-layout-panel-dragover");
44466     },
44467 */
44468     updateBox : function(box)
44469     {
44470         if (!this.bodyEl) {
44471             return; // not rendered yet..
44472         }
44473         
44474         this.box = box;
44475         if(!this.collapsed){
44476             this.el.dom.style.left = box.x + "px";
44477             this.el.dom.style.top = box.y + "px";
44478             this.updateBody(box.width, box.height);
44479         }else{
44480             this.collapsedEl.dom.style.left = box.x + "px";
44481             this.collapsedEl.dom.style.top = box.y + "px";
44482             this.collapsedEl.setSize(box.width, box.height);
44483         }
44484         if(this.tabs){
44485             this.tabs.autoSizeTabs();
44486         }
44487     },
44488
44489     updateBody : function(w, h)
44490     {
44491         if(w !== null){
44492             this.el.setWidth(w);
44493             w -= this.el.getBorderWidth("rl");
44494             if(this.config.adjustments){
44495                 w += this.config.adjustments[0];
44496             }
44497         }
44498         if(h !== null && h > 0){
44499             this.el.setHeight(h);
44500             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44501             h -= this.el.getBorderWidth("tb");
44502             if(this.config.adjustments){
44503                 h += this.config.adjustments[1];
44504             }
44505             this.bodyEl.setHeight(h);
44506             if(this.tabs){
44507                 h = this.tabs.syncHeight(h);
44508             }
44509         }
44510         if(this.panelSize){
44511             w = w !== null ? w : this.panelSize.width;
44512             h = h !== null ? h : this.panelSize.height;
44513         }
44514         if(this.activePanel){
44515             var el = this.activePanel.getEl();
44516             w = w !== null ? w : el.getWidth();
44517             h = h !== null ? h : el.getHeight();
44518             this.panelSize = {width: w, height: h};
44519             this.activePanel.setSize(w, h);
44520         }
44521         if(Roo.isIE && this.tabs){
44522             this.tabs.el.repaint();
44523         }
44524     },
44525
44526     /**
44527      * Returns the container element for this region.
44528      * @return {Roo.Element}
44529      */
44530     getEl : function(){
44531         return this.el;
44532     },
44533
44534     /**
44535      * Hides this region.
44536      */
44537     hide : function(){
44538         //if(!this.collapsed){
44539             this.el.dom.style.left = "-2000px";
44540             this.el.hide();
44541         //}else{
44542          //   this.collapsedEl.dom.style.left = "-2000px";
44543          //   this.collapsedEl.hide();
44544        // }
44545         this.visible = false;
44546         this.fireEvent("visibilitychange", this, false);
44547     },
44548
44549     /**
44550      * Shows this region if it was previously hidden.
44551      */
44552     show : function(){
44553         //if(!this.collapsed){
44554             this.el.show();
44555         //}else{
44556         //    this.collapsedEl.show();
44557        // }
44558         this.visible = true;
44559         this.fireEvent("visibilitychange", this, true);
44560     },
44561 /*
44562     closeClicked : function(){
44563         if(this.activePanel){
44564             this.remove(this.activePanel);
44565         }
44566     },
44567
44568     collapseClick : function(e){
44569         if(this.isSlid){
44570            e.stopPropagation();
44571            this.slideIn();
44572         }else{
44573            e.stopPropagation();
44574            this.slideOut();
44575         }
44576     },
44577 */
44578     /**
44579      * Collapses this region.
44580      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44581      */
44582     /*
44583     collapse : function(skipAnim, skipCheck = false){
44584         if(this.collapsed) {
44585             return;
44586         }
44587         
44588         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44589             
44590             this.collapsed = true;
44591             if(this.split){
44592                 this.split.el.hide();
44593             }
44594             if(this.config.animate && skipAnim !== true){
44595                 this.fireEvent("invalidated", this);
44596                 this.animateCollapse();
44597             }else{
44598                 this.el.setLocation(-20000,-20000);
44599                 this.el.hide();
44600                 this.collapsedEl.show();
44601                 this.fireEvent("collapsed", this);
44602                 this.fireEvent("invalidated", this);
44603             }
44604         }
44605         
44606     },
44607 */
44608     animateCollapse : function(){
44609         // overridden
44610     },
44611
44612     /**
44613      * Expands this region if it was previously collapsed.
44614      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44615      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44616      */
44617     /*
44618     expand : function(e, skipAnim){
44619         if(e) {
44620             e.stopPropagation();
44621         }
44622         if(!this.collapsed || this.el.hasActiveFx()) {
44623             return;
44624         }
44625         if(this.isSlid){
44626             this.afterSlideIn();
44627             skipAnim = true;
44628         }
44629         this.collapsed = false;
44630         if(this.config.animate && skipAnim !== true){
44631             this.animateExpand();
44632         }else{
44633             this.el.show();
44634             if(this.split){
44635                 this.split.el.show();
44636             }
44637             this.collapsedEl.setLocation(-2000,-2000);
44638             this.collapsedEl.hide();
44639             this.fireEvent("invalidated", this);
44640             this.fireEvent("expanded", this);
44641         }
44642     },
44643 */
44644     animateExpand : function(){
44645         // overridden
44646     },
44647
44648     initTabs : function()
44649     {
44650         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44651         
44652         var ts = new Roo.bootstrap.panel.Tabs({
44653             el: this.bodyEl.dom,
44654             region : this,
44655             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
44656             disableTooltips: this.config.disableTabTips,
44657             toolbar : this.config.toolbar
44658         });
44659         
44660         if(this.config.hideTabs){
44661             ts.stripWrap.setDisplayed(false);
44662         }
44663         this.tabs = ts;
44664         ts.resizeTabs = this.config.resizeTabs === true;
44665         ts.minTabWidth = this.config.minTabWidth || 40;
44666         ts.maxTabWidth = this.config.maxTabWidth || 250;
44667         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44668         ts.monitorResize = false;
44669         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44670         ts.bodyEl.addClass('roo-layout-tabs-body');
44671         this.panels.each(this.initPanelAsTab, this);
44672     },
44673
44674     initPanelAsTab : function(panel){
44675         var ti = this.tabs.addTab(
44676             panel.getEl().id,
44677             panel.getTitle(),
44678             null,
44679             this.config.closeOnTab && panel.isClosable(),
44680             panel.tpl
44681         );
44682         if(panel.tabTip !== undefined){
44683             ti.setTooltip(panel.tabTip);
44684         }
44685         ti.on("activate", function(){
44686               this.setActivePanel(panel);
44687         }, this);
44688         
44689         if(this.config.closeOnTab){
44690             ti.on("beforeclose", function(t, e){
44691                 e.cancel = true;
44692                 this.remove(panel);
44693             }, this);
44694         }
44695         
44696         panel.tabItem = ti;
44697         
44698         return ti;
44699     },
44700
44701     updatePanelTitle : function(panel, title)
44702     {
44703         if(this.activePanel == panel){
44704             this.updateTitle(title);
44705         }
44706         if(this.tabs){
44707             var ti = this.tabs.getTab(panel.getEl().id);
44708             ti.setText(title);
44709             if(panel.tabTip !== undefined){
44710                 ti.setTooltip(panel.tabTip);
44711             }
44712         }
44713     },
44714
44715     updateTitle : function(title){
44716         if(this.titleTextEl && !this.config.title){
44717             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44718         }
44719     },
44720
44721     setActivePanel : function(panel)
44722     {
44723         panel = this.getPanel(panel);
44724         if(this.activePanel && this.activePanel != panel){
44725             if(this.activePanel.setActiveState(false) === false){
44726                 return;
44727             }
44728         }
44729         this.activePanel = panel;
44730         panel.setActiveState(true);
44731         if(this.panelSize){
44732             panel.setSize(this.panelSize.width, this.panelSize.height);
44733         }
44734         if(this.closeBtn){
44735             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44736         }
44737         this.updateTitle(panel.getTitle());
44738         if(this.tabs){
44739             this.fireEvent("invalidated", this);
44740         }
44741         this.fireEvent("panelactivated", this, panel);
44742     },
44743
44744     /**
44745      * Shows the specified panel.
44746      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44747      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44748      */
44749     showPanel : function(panel)
44750     {
44751         panel = this.getPanel(panel);
44752         if(panel){
44753             if(this.tabs){
44754                 var tab = this.tabs.getTab(panel.getEl().id);
44755                 if(tab.isHidden()){
44756                     this.tabs.unhideTab(tab.id);
44757                 }
44758                 tab.activate();
44759             }else{
44760                 this.setActivePanel(panel);
44761             }
44762         }
44763         return panel;
44764     },
44765
44766     /**
44767      * Get the active panel for this region.
44768      * @return {Roo.ContentPanel} The active panel or null
44769      */
44770     getActivePanel : function(){
44771         return this.activePanel;
44772     },
44773
44774     validateVisibility : function(){
44775         if(this.panels.getCount() < 1){
44776             this.updateTitle("&#160;");
44777             this.closeBtn.hide();
44778             this.hide();
44779         }else{
44780             if(!this.isVisible()){
44781                 this.show();
44782             }
44783         }
44784     },
44785
44786     /**
44787      * Adds the passed ContentPanel(s) to this region.
44788      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44789      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44790      */
44791     add : function(panel)
44792     {
44793         if(arguments.length > 1){
44794             for(var i = 0, len = arguments.length; i < len; i++) {
44795                 this.add(arguments[i]);
44796             }
44797             return null;
44798         }
44799         
44800         // if we have not been rendered yet, then we can not really do much of this..
44801         if (!this.bodyEl) {
44802             this.unrendered_panels.push(panel);
44803             return panel;
44804         }
44805         
44806         
44807         
44808         
44809         if(this.hasPanel(panel)){
44810             this.showPanel(panel);
44811             return panel;
44812         }
44813         panel.setRegion(this);
44814         this.panels.add(panel);
44815        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44816             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44817             // and hide them... ???
44818             this.bodyEl.dom.appendChild(panel.getEl().dom);
44819             if(panel.background !== true){
44820                 this.setActivePanel(panel);
44821             }
44822             this.fireEvent("paneladded", this, panel);
44823             return panel;
44824         }
44825         */
44826         if(!this.tabs){
44827             this.initTabs();
44828         }else{
44829             this.initPanelAsTab(panel);
44830         }
44831         
44832         
44833         if(panel.background !== true){
44834             this.tabs.activate(panel.getEl().id);
44835         }
44836         this.fireEvent("paneladded", this, panel);
44837         return panel;
44838     },
44839
44840     /**
44841      * Hides the tab for the specified panel.
44842      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44843      */
44844     hidePanel : function(panel){
44845         if(this.tabs && (panel = this.getPanel(panel))){
44846             this.tabs.hideTab(panel.getEl().id);
44847         }
44848     },
44849
44850     /**
44851      * Unhides the tab for a previously hidden panel.
44852      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44853      */
44854     unhidePanel : function(panel){
44855         if(this.tabs && (panel = this.getPanel(panel))){
44856             this.tabs.unhideTab(panel.getEl().id);
44857         }
44858     },
44859
44860     clearPanels : function(){
44861         while(this.panels.getCount() > 0){
44862              this.remove(this.panels.first());
44863         }
44864     },
44865
44866     /**
44867      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44868      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44869      * @param {Boolean} preservePanel Overrides the config preservePanel option
44870      * @return {Roo.ContentPanel} The panel that was removed
44871      */
44872     remove : function(panel, preservePanel)
44873     {
44874         panel = this.getPanel(panel);
44875         if(!panel){
44876             return null;
44877         }
44878         var e = {};
44879         this.fireEvent("beforeremove", this, panel, e);
44880         if(e.cancel === true){
44881             return null;
44882         }
44883         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44884         var panelId = panel.getId();
44885         this.panels.removeKey(panelId);
44886         if(preservePanel){
44887             document.body.appendChild(panel.getEl().dom);
44888         }
44889         if(this.tabs){
44890             this.tabs.removeTab(panel.getEl().id);
44891         }else if (!preservePanel){
44892             this.bodyEl.dom.removeChild(panel.getEl().dom);
44893         }
44894         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44895             var p = this.panels.first();
44896             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44897             tempEl.appendChild(p.getEl().dom);
44898             this.bodyEl.update("");
44899             this.bodyEl.dom.appendChild(p.getEl().dom);
44900             tempEl = null;
44901             this.updateTitle(p.getTitle());
44902             this.tabs = null;
44903             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44904             this.setActivePanel(p);
44905         }
44906         panel.setRegion(null);
44907         if(this.activePanel == panel){
44908             this.activePanel = null;
44909         }
44910         if(this.config.autoDestroy !== false && preservePanel !== true){
44911             try{panel.destroy();}catch(e){}
44912         }
44913         this.fireEvent("panelremoved", this, panel);
44914         return panel;
44915     },
44916
44917     /**
44918      * Returns the TabPanel component used by this region
44919      * @return {Roo.TabPanel}
44920      */
44921     getTabs : function(){
44922         return this.tabs;
44923     },
44924
44925     createTool : function(parentEl, className){
44926         var btn = Roo.DomHelper.append(parentEl, {
44927             tag: "div",
44928             cls: "x-layout-tools-button",
44929             children: [ {
44930                 tag: "div",
44931                 cls: "roo-layout-tools-button-inner " + className,
44932                 html: "&#160;"
44933             }]
44934         }, true);
44935         btn.addClassOnOver("roo-layout-tools-button-over");
44936         return btn;
44937     }
44938 });/*
44939  * Based on:
44940  * Ext JS Library 1.1.1
44941  * Copyright(c) 2006-2007, Ext JS, LLC.
44942  *
44943  * Originally Released Under LGPL - original licence link has changed is not relivant.
44944  *
44945  * Fork - LGPL
44946  * <script type="text/javascript">
44947  */
44948  
44949
44950
44951 /**
44952  * @class Roo.SplitLayoutRegion
44953  * @extends Roo.LayoutRegion
44954  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44955  */
44956 Roo.bootstrap.layout.Split = function(config){
44957     this.cursor = config.cursor;
44958     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44959 };
44960
44961 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44962 {
44963     splitTip : "Drag to resize.",
44964     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44965     useSplitTips : false,
44966
44967     applyConfig : function(config){
44968         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44969     },
44970     
44971     onRender : function(ctr,pos) {
44972         
44973         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44974         if(!this.config.split){
44975             return;
44976         }
44977         if(!this.split){
44978             
44979             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44980                             tag: "div",
44981                             id: this.el.id + "-split",
44982                             cls: "roo-layout-split roo-layout-split-"+this.position,
44983                             html: "&#160;"
44984             });
44985             /** The SplitBar for this region 
44986             * @type Roo.SplitBar */
44987             // does not exist yet...
44988             Roo.log([this.position, this.orientation]);
44989             
44990             this.split = new Roo.bootstrap.SplitBar({
44991                 dragElement : splitEl,
44992                 resizingElement: this.el,
44993                 orientation : this.orientation
44994             });
44995             
44996             this.split.on("moved", this.onSplitMove, this);
44997             this.split.useShim = this.config.useShim === true;
44998             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44999             if(this.useSplitTips){
45000                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45001             }
45002             //if(config.collapsible){
45003             //    this.split.el.on("dblclick", this.collapse,  this);
45004             //}
45005         }
45006         if(typeof this.config.minSize != "undefined"){
45007             this.split.minSize = this.config.minSize;
45008         }
45009         if(typeof this.config.maxSize != "undefined"){
45010             this.split.maxSize = this.config.maxSize;
45011         }
45012         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
45013             this.hideSplitter();
45014         }
45015         
45016     },
45017
45018     getHMaxSize : function(){
45019          var cmax = this.config.maxSize || 10000;
45020          var center = this.mgr.getRegion("center");
45021          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45022     },
45023
45024     getVMaxSize : function(){
45025          var cmax = this.config.maxSize || 10000;
45026          var center = this.mgr.getRegion("center");
45027          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45028     },
45029
45030     onSplitMove : function(split, newSize){
45031         this.fireEvent("resized", this, newSize);
45032     },
45033     
45034     /** 
45035      * Returns the {@link Roo.SplitBar} for this region.
45036      * @return {Roo.SplitBar}
45037      */
45038     getSplitBar : function(){
45039         return this.split;
45040     },
45041     
45042     hide : function(){
45043         this.hideSplitter();
45044         Roo.bootstrap.layout.Split.superclass.hide.call(this);
45045     },
45046
45047     hideSplitter : function(){
45048         if(this.split){
45049             this.split.el.setLocation(-2000,-2000);
45050             this.split.el.hide();
45051         }
45052     },
45053
45054     show : function(){
45055         if(this.split){
45056             this.split.el.show();
45057         }
45058         Roo.bootstrap.layout.Split.superclass.show.call(this);
45059     },
45060     
45061     beforeSlide: function(){
45062         if(Roo.isGecko){// firefox overflow auto bug workaround
45063             this.bodyEl.clip();
45064             if(this.tabs) {
45065                 this.tabs.bodyEl.clip();
45066             }
45067             if(this.activePanel){
45068                 this.activePanel.getEl().clip();
45069                 
45070                 if(this.activePanel.beforeSlide){
45071                     this.activePanel.beforeSlide();
45072                 }
45073             }
45074         }
45075     },
45076     
45077     afterSlide : function(){
45078         if(Roo.isGecko){// firefox overflow auto bug workaround
45079             this.bodyEl.unclip();
45080             if(this.tabs) {
45081                 this.tabs.bodyEl.unclip();
45082             }
45083             if(this.activePanel){
45084                 this.activePanel.getEl().unclip();
45085                 if(this.activePanel.afterSlide){
45086                     this.activePanel.afterSlide();
45087                 }
45088             }
45089         }
45090     },
45091
45092     initAutoHide : function(){
45093         if(this.autoHide !== false){
45094             if(!this.autoHideHd){
45095                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45096                 this.autoHideHd = {
45097                     "mouseout": function(e){
45098                         if(!e.within(this.el, true)){
45099                             st.delay(500);
45100                         }
45101                     },
45102                     "mouseover" : function(e){
45103                         st.cancel();
45104                     },
45105                     scope : this
45106                 };
45107             }
45108             this.el.on(this.autoHideHd);
45109         }
45110     },
45111
45112     clearAutoHide : function(){
45113         if(this.autoHide !== false){
45114             this.el.un("mouseout", this.autoHideHd.mouseout);
45115             this.el.un("mouseover", this.autoHideHd.mouseover);
45116         }
45117     },
45118
45119     clearMonitor : function(){
45120         Roo.get(document).un("click", this.slideInIf, this);
45121     },
45122
45123     // these names are backwards but not changed for compat
45124     slideOut : function(){
45125         if(this.isSlid || this.el.hasActiveFx()){
45126             return;
45127         }
45128         this.isSlid = true;
45129         if(this.collapseBtn){
45130             this.collapseBtn.hide();
45131         }
45132         this.closeBtnState = this.closeBtn.getStyle('display');
45133         this.closeBtn.hide();
45134         if(this.stickBtn){
45135             this.stickBtn.show();
45136         }
45137         this.el.show();
45138         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45139         this.beforeSlide();
45140         this.el.setStyle("z-index", 10001);
45141         this.el.slideIn(this.getSlideAnchor(), {
45142             callback: function(){
45143                 this.afterSlide();
45144                 this.initAutoHide();
45145                 Roo.get(document).on("click", this.slideInIf, this);
45146                 this.fireEvent("slideshow", this);
45147             },
45148             scope: this,
45149             block: true
45150         });
45151     },
45152
45153     afterSlideIn : function(){
45154         this.clearAutoHide();
45155         this.isSlid = false;
45156         this.clearMonitor();
45157         this.el.setStyle("z-index", "");
45158         if(this.collapseBtn){
45159             this.collapseBtn.show();
45160         }
45161         this.closeBtn.setStyle('display', this.closeBtnState);
45162         if(this.stickBtn){
45163             this.stickBtn.hide();
45164         }
45165         this.fireEvent("slidehide", this);
45166     },
45167
45168     slideIn : function(cb){
45169         if(!this.isSlid || this.el.hasActiveFx()){
45170             Roo.callback(cb);
45171             return;
45172         }
45173         this.isSlid = false;
45174         this.beforeSlide();
45175         this.el.slideOut(this.getSlideAnchor(), {
45176             callback: function(){
45177                 this.el.setLeftTop(-10000, -10000);
45178                 this.afterSlide();
45179                 this.afterSlideIn();
45180                 Roo.callback(cb);
45181             },
45182             scope: this,
45183             block: true
45184         });
45185     },
45186     
45187     slideInIf : function(e){
45188         if(!e.within(this.el)){
45189             this.slideIn();
45190         }
45191     },
45192
45193     animateCollapse : function(){
45194         this.beforeSlide();
45195         this.el.setStyle("z-index", 20000);
45196         var anchor = this.getSlideAnchor();
45197         this.el.slideOut(anchor, {
45198             callback : function(){
45199                 this.el.setStyle("z-index", "");
45200                 this.collapsedEl.slideIn(anchor, {duration:.3});
45201                 this.afterSlide();
45202                 this.el.setLocation(-10000,-10000);
45203                 this.el.hide();
45204                 this.fireEvent("collapsed", this);
45205             },
45206             scope: this,
45207             block: true
45208         });
45209     },
45210
45211     animateExpand : function(){
45212         this.beforeSlide();
45213         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45214         this.el.setStyle("z-index", 20000);
45215         this.collapsedEl.hide({
45216             duration:.1
45217         });
45218         this.el.slideIn(this.getSlideAnchor(), {
45219             callback : function(){
45220                 this.el.setStyle("z-index", "");
45221                 this.afterSlide();
45222                 if(this.split){
45223                     this.split.el.show();
45224                 }
45225                 this.fireEvent("invalidated", this);
45226                 this.fireEvent("expanded", this);
45227             },
45228             scope: this,
45229             block: true
45230         });
45231     },
45232
45233     anchors : {
45234         "west" : "left",
45235         "east" : "right",
45236         "north" : "top",
45237         "south" : "bottom"
45238     },
45239
45240     sanchors : {
45241         "west" : "l",
45242         "east" : "r",
45243         "north" : "t",
45244         "south" : "b"
45245     },
45246
45247     canchors : {
45248         "west" : "tl-tr",
45249         "east" : "tr-tl",
45250         "north" : "tl-bl",
45251         "south" : "bl-tl"
45252     },
45253
45254     getAnchor : function(){
45255         return this.anchors[this.position];
45256     },
45257
45258     getCollapseAnchor : function(){
45259         return this.canchors[this.position];
45260     },
45261
45262     getSlideAnchor : function(){
45263         return this.sanchors[this.position];
45264     },
45265
45266     getAlignAdj : function(){
45267         var cm = this.cmargins;
45268         switch(this.position){
45269             case "west":
45270                 return [0, 0];
45271             break;
45272             case "east":
45273                 return [0, 0];
45274             break;
45275             case "north":
45276                 return [0, 0];
45277             break;
45278             case "south":
45279                 return [0, 0];
45280             break;
45281         }
45282     },
45283
45284     getExpandAdj : function(){
45285         var c = this.collapsedEl, cm = this.cmargins;
45286         switch(this.position){
45287             case "west":
45288                 return [-(cm.right+c.getWidth()+cm.left), 0];
45289             break;
45290             case "east":
45291                 return [cm.right+c.getWidth()+cm.left, 0];
45292             break;
45293             case "north":
45294                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45295             break;
45296             case "south":
45297                 return [0, cm.top+cm.bottom+c.getHeight()];
45298             break;
45299         }
45300     }
45301 });/*
45302  * Based on:
45303  * Ext JS Library 1.1.1
45304  * Copyright(c) 2006-2007, Ext JS, LLC.
45305  *
45306  * Originally Released Under LGPL - original licence link has changed is not relivant.
45307  *
45308  * Fork - LGPL
45309  * <script type="text/javascript">
45310  */
45311 /*
45312  * These classes are private internal classes
45313  */
45314 Roo.bootstrap.layout.Center = function(config){
45315     config.region = "center";
45316     Roo.bootstrap.layout.Region.call(this, config);
45317     this.visible = true;
45318     this.minWidth = config.minWidth || 20;
45319     this.minHeight = config.minHeight || 20;
45320 };
45321
45322 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
45323     hide : function(){
45324         // center panel can't be hidden
45325     },
45326     
45327     show : function(){
45328         // center panel can't be hidden
45329     },
45330     
45331     getMinWidth: function(){
45332         return this.minWidth;
45333     },
45334     
45335     getMinHeight: function(){
45336         return this.minHeight;
45337     }
45338 });
45339
45340
45341
45342
45343  
45344
45345
45346
45347
45348
45349
45350 Roo.bootstrap.layout.North = function(config)
45351 {
45352     config.region = 'north';
45353     config.cursor = 'n-resize';
45354     
45355     Roo.bootstrap.layout.Split.call(this, config);
45356     
45357     
45358     if(this.split){
45359         this.split.placement = Roo.bootstrap.SplitBar.TOP;
45360         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45361         this.split.el.addClass("roo-layout-split-v");
45362     }
45363     //var size = config.initialSize || config.height;
45364     //if(this.el && typeof size != "undefined"){
45365     //    this.el.setHeight(size);
45366     //}
45367 };
45368 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45369 {
45370     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45371      
45372      
45373     onRender : function(ctr, pos)
45374     {
45375         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45376         var size = this.config.initialSize || this.config.height;
45377         if(this.el && typeof size != "undefined"){
45378             this.el.setHeight(size);
45379         }
45380     
45381     },
45382     
45383     getBox : function(){
45384         if(this.collapsed){
45385             return this.collapsedEl.getBox();
45386         }
45387         var box = this.el.getBox();
45388         if(this.split){
45389             box.height += this.split.el.getHeight();
45390         }
45391         return box;
45392     },
45393     
45394     updateBox : function(box){
45395         if(this.split && !this.collapsed){
45396             box.height -= this.split.el.getHeight();
45397             this.split.el.setLeft(box.x);
45398             this.split.el.setTop(box.y+box.height);
45399             this.split.el.setWidth(box.width);
45400         }
45401         if(this.collapsed){
45402             this.updateBody(box.width, null);
45403         }
45404         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45405     }
45406 });
45407
45408
45409
45410
45411
45412 Roo.bootstrap.layout.South = function(config){
45413     config.region = 'south';
45414     config.cursor = 's-resize';
45415     Roo.bootstrap.layout.Split.call(this, config);
45416     if(this.split){
45417         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45418         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45419         this.split.el.addClass("roo-layout-split-v");
45420     }
45421     
45422 };
45423
45424 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45425     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45426     
45427     onRender : function(ctr, pos)
45428     {
45429         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45430         var size = this.config.initialSize || this.config.height;
45431         if(this.el && typeof size != "undefined"){
45432             this.el.setHeight(size);
45433         }
45434     
45435     },
45436     
45437     getBox : function(){
45438         if(this.collapsed){
45439             return this.collapsedEl.getBox();
45440         }
45441         var box = this.el.getBox();
45442         if(this.split){
45443             var sh = this.split.el.getHeight();
45444             box.height += sh;
45445             box.y -= sh;
45446         }
45447         return box;
45448     },
45449     
45450     updateBox : function(box){
45451         if(this.split && !this.collapsed){
45452             var sh = this.split.el.getHeight();
45453             box.height -= sh;
45454             box.y += sh;
45455             this.split.el.setLeft(box.x);
45456             this.split.el.setTop(box.y-sh);
45457             this.split.el.setWidth(box.width);
45458         }
45459         if(this.collapsed){
45460             this.updateBody(box.width, null);
45461         }
45462         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45463     }
45464 });
45465
45466 Roo.bootstrap.layout.East = function(config){
45467     config.region = "east";
45468     config.cursor = "e-resize";
45469     Roo.bootstrap.layout.Split.call(this, config);
45470     if(this.split){
45471         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45472         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45473         this.split.el.addClass("roo-layout-split-h");
45474     }
45475     
45476 };
45477 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45478     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45479     
45480     onRender : function(ctr, pos)
45481     {
45482         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45483         var size = this.config.initialSize || this.config.width;
45484         if(this.el && typeof size != "undefined"){
45485             this.el.setWidth(size);
45486         }
45487     
45488     },
45489     
45490     getBox : function(){
45491         if(this.collapsed){
45492             return this.collapsedEl.getBox();
45493         }
45494         var box = this.el.getBox();
45495         if(this.split){
45496             var sw = this.split.el.getWidth();
45497             box.width += sw;
45498             box.x -= sw;
45499         }
45500         return box;
45501     },
45502
45503     updateBox : function(box){
45504         if(this.split && !this.collapsed){
45505             var sw = this.split.el.getWidth();
45506             box.width -= sw;
45507             this.split.el.setLeft(box.x);
45508             this.split.el.setTop(box.y);
45509             this.split.el.setHeight(box.height);
45510             box.x += sw;
45511         }
45512         if(this.collapsed){
45513             this.updateBody(null, box.height);
45514         }
45515         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45516     }
45517 });
45518
45519 Roo.bootstrap.layout.West = function(config){
45520     config.region = "west";
45521     config.cursor = "w-resize";
45522     
45523     Roo.bootstrap.layout.Split.call(this, config);
45524     if(this.split){
45525         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45526         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45527         this.split.el.addClass("roo-layout-split-h");
45528     }
45529     
45530 };
45531 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45532     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45533     
45534     onRender: function(ctr, pos)
45535     {
45536         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45537         var size = this.config.initialSize || this.config.width;
45538         if(typeof size != "undefined"){
45539             this.el.setWidth(size);
45540         }
45541     },
45542     
45543     getBox : function(){
45544         if(this.collapsed){
45545             return this.collapsedEl.getBox();
45546         }
45547         var box = this.el.getBox();
45548         if (box.width == 0) {
45549             box.width = this.config.width; // kludge?
45550         }
45551         if(this.split){
45552             box.width += this.split.el.getWidth();
45553         }
45554         return box;
45555     },
45556     
45557     updateBox : function(box){
45558         if(this.split && !this.collapsed){
45559             var sw = this.split.el.getWidth();
45560             box.width -= sw;
45561             this.split.el.setLeft(box.x+box.width);
45562             this.split.el.setTop(box.y);
45563             this.split.el.setHeight(box.height);
45564         }
45565         if(this.collapsed){
45566             this.updateBody(null, box.height);
45567         }
45568         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45569     }
45570 });/*
45571  * Based on:
45572  * Ext JS Library 1.1.1
45573  * Copyright(c) 2006-2007, Ext JS, LLC.
45574  *
45575  * Originally Released Under LGPL - original licence link has changed is not relivant.
45576  *
45577  * Fork - LGPL
45578  * <script type="text/javascript">
45579  */
45580 /**
45581  * @class Roo.bootstrap.paenl.Content
45582  * @extends Roo.util.Observable
45583  * @children Roo.bootstrap.Component
45584  * @parent builder Roo.bootstrap.layout.Border
45585  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45586  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45587  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45588  * @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
45589  * @cfg {Boolean}   closable      True if the panel can be closed/removed
45590  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
45591  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45592  * @cfg {Toolbar}   toolbar       A toolbar for this panel
45593  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
45594  * @cfg {String} title          The title for this panel
45595  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45596  * @cfg {String} url            Calls {@link #setUrl} with this value
45597  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45598  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
45599  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
45600  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
45601  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
45602  * @cfg {Boolean} badges render the badges
45603  * @cfg {String} cls  extra classes to use  
45604  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45605  
45606  * @constructor
45607  * Create a new ContentPanel.
45608  * @param {String/Object} config A string to set only the title or a config object
45609  
45610  */
45611 Roo.bootstrap.panel.Content = function( config){
45612     
45613     this.tpl = config.tpl || false;
45614     
45615     var el = config.el;
45616     var content = config.content;
45617
45618     if(config.autoCreate){ // xtype is available if this is called from factory
45619         el = Roo.id();
45620     }
45621     this.el = Roo.get(el);
45622     if(!this.el && config && config.autoCreate){
45623         if(typeof config.autoCreate == "object"){
45624             if(!config.autoCreate.id){
45625                 config.autoCreate.id = config.id||el;
45626             }
45627             this.el = Roo.DomHelper.append(document.body,
45628                         config.autoCreate, true);
45629         }else{
45630             var elcfg =  {
45631                 tag: "div",
45632                 cls: (config.cls || '') +
45633                     (config.background ? ' bg-' + config.background : '') +
45634                     " roo-layout-inactive-content",
45635                 id: config.id||el
45636             };
45637             if (config.iframe) {
45638                 elcfg.cn = [
45639                     {
45640                         tag : 'iframe',
45641                         style : 'border: 0px',
45642                         src : 'data:text/html,%3Cbody%3E%3C%2Fbody%3E'
45643                     }
45644                 ];
45645             }
45646               
45647             if (config.html) {
45648                 elcfg.html = config.html;
45649                 
45650             }
45651                         
45652             this.el = Roo.DomHelper.append(document.body, elcfg , true);
45653             if (config.iframe) {
45654                 this.iframeEl = this.el.select('iframe',true).first();
45655             }
45656             
45657         }
45658     } 
45659     this.closable = false;
45660     this.loaded = false;
45661     this.active = false;
45662    
45663       
45664     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45665         
45666         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45667         
45668         this.wrapEl = this.el; //this.el.wrap();
45669         var ti = [];
45670         if (config.toolbar.items) {
45671             ti = config.toolbar.items ;
45672             delete config.toolbar.items ;
45673         }
45674         
45675         var nitems = [];
45676         this.toolbar.render(this.wrapEl, 'before');
45677         for(var i =0;i < ti.length;i++) {
45678           //  Roo.log(['add child', items[i]]);
45679             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45680         }
45681         this.toolbar.items = nitems;
45682         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45683         delete config.toolbar;
45684         
45685     }
45686     /*
45687     // xtype created footer. - not sure if will work as we normally have to render first..
45688     if (this.footer && !this.footer.el && this.footer.xtype) {
45689         if (!this.wrapEl) {
45690             this.wrapEl = this.el.wrap();
45691         }
45692     
45693         this.footer.container = this.wrapEl.createChild();
45694          
45695         this.footer = Roo.factory(this.footer, Roo);
45696         
45697     }
45698     */
45699     
45700      if(typeof config == "string"){
45701         this.title = config;
45702     }else{
45703         Roo.apply(this, config);
45704     }
45705     
45706     if(this.resizeEl){
45707         this.resizeEl = Roo.get(this.resizeEl, true);
45708     }else{
45709         this.resizeEl = this.el;
45710     }
45711     // handle view.xtype
45712     
45713  
45714     
45715     
45716     this.addEvents({
45717         /**
45718          * @event activate
45719          * Fires when this panel is activated. 
45720          * @param {Roo.ContentPanel} this
45721          */
45722         "activate" : true,
45723         /**
45724          * @event deactivate
45725          * Fires when this panel is activated. 
45726          * @param {Roo.ContentPanel} this
45727          */
45728         "deactivate" : true,
45729
45730         /**
45731          * @event resize
45732          * Fires when this panel is resized if fitToFrame is true.
45733          * @param {Roo.ContentPanel} this
45734          * @param {Number} width The width after any component adjustments
45735          * @param {Number} height The height after any component adjustments
45736          */
45737         "resize" : true,
45738         
45739          /**
45740          * @event render
45741          * Fires when this tab is created
45742          * @param {Roo.ContentPanel} this
45743          */
45744         "render" : true,
45745         
45746           /**
45747          * @event scroll
45748          * Fires when this content is scrolled
45749          * @param {Roo.ContentPanel} this
45750          * @param {Event} scrollEvent
45751          */
45752         "scroll" : true
45753         
45754         
45755         
45756     });
45757     
45758
45759     
45760     
45761     if(this.autoScroll && !this.iframe){
45762         this.resizeEl.setStyle("overflow", "auto");
45763         this.resizeEl.on('scroll', this.onScroll, this);
45764     } else {
45765         // fix randome scrolling
45766         //this.el.on('scroll', function() {
45767         //    Roo.log('fix random scolling');
45768         //    this.scrollTo('top',0); 
45769         //});
45770     }
45771     content = content || this.content;
45772     if(content){
45773         this.setContent(content);
45774     }
45775     if(config && config.url){
45776         this.setUrl(this.url, this.params, this.loadOnce);
45777     }
45778     
45779     
45780     
45781     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45782     
45783     if (this.view && typeof(this.view.xtype) != 'undefined') {
45784         this.view.el = this.el.appendChild(document.createElement("div"));
45785         this.view = Roo.factory(this.view); 
45786         this.view.render  &&  this.view.render(false, '');  
45787     }
45788     
45789     
45790     this.fireEvent('render', this);
45791 };
45792
45793 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45794     
45795     cls : '',
45796     background : '',
45797     
45798     tabTip : '',
45799     
45800     iframe : false,
45801     iframeEl : false,
45802     
45803     /* Resize Element - use this to work out scroll etc. */
45804     resizeEl : false,
45805     
45806     setRegion : function(region){
45807         this.region = region;
45808         this.setActiveClass(region && !this.background);
45809     },
45810     
45811     
45812     setActiveClass: function(state)
45813     {
45814         if(state){
45815            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45816            this.el.setStyle('position','relative');
45817         }else{
45818            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45819            this.el.setStyle('position', 'absolute');
45820         } 
45821     },
45822     
45823     /**
45824      * Returns the toolbar for this Panel if one was configured. 
45825      * @return {Roo.Toolbar} 
45826      */
45827     getToolbar : function(){
45828         return this.toolbar;
45829     },
45830     
45831     setActiveState : function(active)
45832     {
45833         this.active = active;
45834         this.setActiveClass(active);
45835         if(!active){
45836             if(this.fireEvent("deactivate", this) === false){
45837                 return false;
45838             }
45839             return true;
45840         }
45841         this.fireEvent("activate", this);
45842         return true;
45843     },
45844     /**
45845      * Updates this panel's element (not for iframe)
45846      * @param {String} content The new content
45847      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45848     */
45849     setContent : function(content, loadScripts){
45850         if (this.iframe) {
45851             return;
45852         }
45853         
45854         this.el.update(content, loadScripts);
45855     },
45856
45857     ignoreResize : function(w, h)
45858     {
45859         //return false; // always resize?
45860         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45861             return true;
45862         }else{
45863             this.lastSize = {width: w, height: h};
45864             return false;
45865         }
45866     },
45867     /**
45868      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45869      * @return {Roo.UpdateManager} The UpdateManager
45870      */
45871     getUpdateManager : function(){
45872         if (this.iframe) {
45873             return false;
45874         }
45875         return this.el.getUpdateManager();
45876     },
45877      /**
45878      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45879      * Does not work with IFRAME contents
45880      * @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:
45881 <pre><code>
45882 panel.load({
45883     url: "your-url.php",
45884     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45885     callback: yourFunction,
45886     scope: yourObject, //(optional scope)
45887     discardUrl: false,
45888     nocache: false,
45889     text: "Loading...",
45890     timeout: 30,
45891     scripts: false
45892 });
45893 </code></pre>
45894      
45895      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45896      * 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.
45897      * @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}
45898      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45899      * @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.
45900      * @return {Roo.ContentPanel} this
45901      */
45902     load : function(){
45903         
45904         if (this.iframe) {
45905             return this;
45906         }
45907         
45908         var um = this.el.getUpdateManager();
45909         um.update.apply(um, arguments);
45910         return this;
45911     },
45912
45913
45914     /**
45915      * 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.
45916      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45917      * @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)
45918      * @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)
45919      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45920      */
45921     setUrl : function(url, params, loadOnce){
45922         if (this.iframe) {
45923             this.iframeEl.dom.src = url;
45924             return false;
45925         }
45926         
45927         if(this.refreshDelegate){
45928             this.removeListener("activate", this.refreshDelegate);
45929         }
45930         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45931         this.on("activate", this.refreshDelegate);
45932         return this.el.getUpdateManager();
45933     },
45934     
45935     _handleRefresh : function(url, params, loadOnce){
45936         if(!loadOnce || !this.loaded){
45937             var updater = this.el.getUpdateManager();
45938             updater.update(url, params, this._setLoaded.createDelegate(this));
45939         }
45940     },
45941     
45942     _setLoaded : function(){
45943         this.loaded = true;
45944     }, 
45945     
45946     /**
45947      * Returns this panel's id
45948      * @return {String} 
45949      */
45950     getId : function(){
45951         return this.el.id;
45952     },
45953     
45954     /** 
45955      * Returns this panel's element - used by regiosn to add.
45956      * @return {Roo.Element} 
45957      */
45958     getEl : function(){
45959         return this.wrapEl || this.el;
45960     },
45961     
45962    
45963     
45964     adjustForComponents : function(width, height)
45965     {
45966         //Roo.log('adjustForComponents ');
45967         if(this.resizeEl != this.el){
45968             width -= this.el.getFrameWidth('lr');
45969             height -= this.el.getFrameWidth('tb');
45970         }
45971         if(this.toolbar){
45972             var te = this.toolbar.getEl();
45973             te.setWidth(width);
45974             height -= te.getHeight();
45975         }
45976         if(this.footer){
45977             var te = this.footer.getEl();
45978             te.setWidth(width);
45979             height -= te.getHeight();
45980         }
45981         
45982         
45983         if(this.adjustments){
45984             width += this.adjustments[0];
45985             height += this.adjustments[1];
45986         }
45987         return {"width": width, "height": height};
45988     },
45989     
45990     setSize : function(width, height){
45991         if(this.fitToFrame && !this.ignoreResize(width, height)){
45992             if(this.fitContainer && this.resizeEl != this.el){
45993                 this.el.setSize(width, height);
45994             }
45995             var size = this.adjustForComponents(width, height);
45996             if (this.iframe) {
45997                 this.iframeEl.setSize(width,height);
45998             }
45999             
46000             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46001             this.fireEvent('resize', this, size.width, size.height);
46002             
46003             
46004         }
46005     },
46006     
46007     /**
46008      * Returns this panel's title
46009      * @return {String} 
46010      */
46011     getTitle : function(){
46012         
46013         if (typeof(this.title) != 'object') {
46014             return this.title;
46015         }
46016         
46017         var t = '';
46018         for (var k in this.title) {
46019             if (!this.title.hasOwnProperty(k)) {
46020                 continue;
46021             }
46022             
46023             if (k.indexOf('-') >= 0) {
46024                 var s = k.split('-');
46025                 for (var i = 0; i<s.length; i++) {
46026                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
46027                 }
46028             } else {
46029                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
46030             }
46031         }
46032         return t;
46033     },
46034     
46035     /**
46036      * Set this panel's title
46037      * @param {String} title
46038      */
46039     setTitle : function(title){
46040         this.title = title;
46041         if(this.region){
46042             this.region.updatePanelTitle(this, title);
46043         }
46044     },
46045     
46046     /**
46047      * Returns true is this panel was configured to be closable
46048      * @return {Boolean} 
46049      */
46050     isClosable : function(){
46051         return this.closable;
46052     },
46053     
46054     beforeSlide : function(){
46055         this.el.clip();
46056         this.resizeEl.clip();
46057     },
46058     
46059     afterSlide : function(){
46060         this.el.unclip();
46061         this.resizeEl.unclip();
46062     },
46063     
46064     /**
46065      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46066      *   Will fail silently if the {@link #setUrl} method has not been called.
46067      *   This does not activate the panel, just updates its content.
46068      */
46069     refresh : function(){
46070         if(this.refreshDelegate){
46071            this.loaded = false;
46072            this.refreshDelegate();
46073         }
46074     },
46075     
46076     /**
46077      * Destroys this panel
46078      */
46079     destroy : function(){
46080         this.el.removeAllListeners();
46081         var tempEl = document.createElement("span");
46082         tempEl.appendChild(this.el.dom);
46083         tempEl.innerHTML = "";
46084         this.el.remove();
46085         this.el = null;
46086     },
46087     
46088     /**
46089      * form - if the content panel contains a form - this is a reference to it.
46090      * @type {Roo.form.Form}
46091      */
46092     form : false,
46093     /**
46094      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46095      *    This contains a reference to it.
46096      * @type {Roo.View}
46097      */
46098     view : false,
46099     
46100       /**
46101      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46102      * <pre><code>
46103
46104 layout.addxtype({
46105        xtype : 'Form',
46106        items: [ .... ]
46107    }
46108 );
46109
46110 </code></pre>
46111      * @param {Object} cfg Xtype definition of item to add.
46112      */
46113     
46114     
46115     getChildContainer: function () {
46116         return this.getEl();
46117     },
46118     
46119     
46120     onScroll : function(e)
46121     {
46122         this.fireEvent('scroll', this, e);
46123     }
46124     
46125     
46126     /*
46127         var  ret = new Roo.factory(cfg);
46128         return ret;
46129         
46130         
46131         // add form..
46132         if (cfg.xtype.match(/^Form$/)) {
46133             
46134             var el;
46135             //if (this.footer) {
46136             //    el = this.footer.container.insertSibling(false, 'before');
46137             //} else {
46138                 el = this.el.createChild();
46139             //}
46140
46141             this.form = new  Roo.form.Form(cfg);
46142             
46143             
46144             if ( this.form.allItems.length) {
46145                 this.form.render(el.dom);
46146             }
46147             return this.form;
46148         }
46149         // should only have one of theses..
46150         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46151             // views.. should not be just added - used named prop 'view''
46152             
46153             cfg.el = this.el.appendChild(document.createElement("div"));
46154             // factory?
46155             
46156             var ret = new Roo.factory(cfg);
46157              
46158              ret.render && ret.render(false, ''); // render blank..
46159             this.view = ret;
46160             return ret;
46161         }
46162         return false;
46163     }
46164     \*/
46165 });
46166  
46167 /**
46168  * @class Roo.bootstrap.panel.Grid
46169  * @extends Roo.bootstrap.panel.Content
46170  * @constructor
46171  * Create a new GridPanel.
46172  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
46173  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
46174  * @param {Object} config A the config object
46175   
46176  */
46177
46178
46179
46180 Roo.bootstrap.panel.Grid = function(config)
46181 {
46182     
46183       
46184     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46185         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
46186
46187     config.el = this.wrapper;
46188     //this.el = this.wrapper;
46189     
46190       if (config.container) {
46191         // ctor'ed from a Border/panel.grid
46192         
46193         
46194         this.wrapper.setStyle("overflow", "hidden");
46195         this.wrapper.addClass('roo-grid-container');
46196
46197     }
46198     
46199     
46200     if(config.toolbar){
46201         var tool_el = this.wrapper.createChild();    
46202         this.toolbar = Roo.factory(config.toolbar);
46203         var ti = [];
46204         if (config.toolbar.items) {
46205             ti = config.toolbar.items ;
46206             delete config.toolbar.items ;
46207         }
46208         
46209         var nitems = [];
46210         this.toolbar.render(tool_el);
46211         for(var i =0;i < ti.length;i++) {
46212           //  Roo.log(['add child', items[i]]);
46213             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
46214         }
46215         this.toolbar.items = nitems;
46216         
46217         delete config.toolbar;
46218     }
46219     
46220     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
46221     config.grid.scrollBody = true;;
46222     config.grid.monitorWindowResize = false; // turn off autosizing
46223     config.grid.autoHeight = false;
46224     config.grid.autoWidth = false;
46225     
46226     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
46227     
46228     if (config.background) {
46229         // render grid on panel activation (if panel background)
46230         this.on('activate', function(gp) {
46231             if (!gp.grid.rendered) {
46232                 gp.grid.render(this.wrapper);
46233                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
46234             }
46235         });
46236             
46237     } else {
46238         this.grid.render(this.wrapper);
46239         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
46240
46241     }
46242     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
46243     // ??? needed ??? config.el = this.wrapper;
46244     
46245     
46246     
46247   
46248     // xtype created footer. - not sure if will work as we normally have to render first..
46249     if (this.footer && !this.footer.el && this.footer.xtype) {
46250         
46251         var ctr = this.grid.getView().getFooterPanel(true);
46252         this.footer.dataSource = this.grid.dataSource;
46253         this.footer = Roo.factory(this.footer, Roo);
46254         this.footer.render(ctr);
46255         
46256     }
46257     
46258     
46259     
46260     
46261      
46262 };
46263
46264 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
46265 {
46266   
46267     getId : function(){
46268         return this.grid.id;
46269     },
46270     
46271     /**
46272      * Returns the grid for this panel
46273      * @return {Roo.bootstrap.Table} 
46274      */
46275     getGrid : function(){
46276         return this.grid;    
46277     },
46278     
46279     setSize : function(width, height)
46280     {
46281      
46282         //if(!this.ignoreResize(width, height)){
46283             var grid = this.grid;
46284             var size = this.adjustForComponents(width, height);
46285             // tfoot is not a footer?
46286           
46287             
46288             var gridel = grid.getGridEl();
46289             gridel.setSize(size.width, size.height);
46290             
46291             var tbd = grid.getGridEl().select('tbody', true).first();
46292             var thd = grid.getGridEl().select('thead',true).first();
46293             var tbf= grid.getGridEl().select('tfoot', true).first();
46294
46295             if (tbf) {
46296                 size.height -= tbf.getHeight();
46297             }
46298             if (thd) {
46299                 size.height -= thd.getHeight();
46300             }
46301             
46302             tbd.setSize(size.width, size.height );
46303             // this is for the account management tab -seems to work there.
46304             var thd = grid.getGridEl().select('thead',true).first();
46305             //if (tbd) {
46306             //    tbd.setSize(size.width, size.height - thd.getHeight());
46307             //}
46308              
46309             grid.autoSize();
46310         //}
46311    
46312     },
46313      
46314     
46315     
46316     beforeSlide : function(){
46317         this.grid.getView().scroller.clip();
46318     },
46319     
46320     afterSlide : function(){
46321         this.grid.getView().scroller.unclip();
46322     },
46323     
46324     destroy : function(){
46325         this.grid.destroy();
46326         delete this.grid;
46327         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
46328     }
46329 });
46330
46331 /**
46332  * @class Roo.bootstrap.panel.Nest
46333  * @extends Roo.bootstrap.panel.Content
46334  * @constructor
46335  * Create a new Panel, that can contain a layout.Border.
46336  * 
46337  * 
46338  * @param {String/Object} config A string to set only the title or a config object
46339  */
46340 Roo.bootstrap.panel.Nest = function(config)
46341 {
46342     // construct with only one argument..
46343     /* FIXME - implement nicer consturctors
46344     if (layout.layout) {
46345         config = layout;
46346         layout = config.layout;
46347         delete config.layout;
46348     }
46349     if (layout.xtype && !layout.getEl) {
46350         // then layout needs constructing..
46351         layout = Roo.factory(layout, Roo);
46352     }
46353     */
46354     
46355     config.el =  config.layout.getEl();
46356     
46357     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46358     
46359     config.layout.monitorWindowResize = false; // turn off autosizing
46360     this.layout = config.layout;
46361     this.layout.getEl().addClass("roo-layout-nested-layout");
46362     this.layout.parent = this;
46363     
46364     
46365     
46366     
46367 };
46368
46369 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46370     /**
46371     * @cfg {Roo.BorderLayout} layout The layout for this panel
46372     */
46373     layout : false,
46374
46375     setSize : function(width, height){
46376         if(!this.ignoreResize(width, height)){
46377             var size = this.adjustForComponents(width, height);
46378             var el = this.layout.getEl();
46379             if (size.height < 1) {
46380                 el.setWidth(size.width);   
46381             } else {
46382                 el.setSize(size.width, size.height);
46383             }
46384             var touch = el.dom.offsetWidth;
46385             this.layout.layout();
46386             // ie requires a double layout on the first pass
46387             if(Roo.isIE && !this.initialized){
46388                 this.initialized = true;
46389                 this.layout.layout();
46390             }
46391         }
46392     },
46393     
46394     // activate all subpanels if not currently active..
46395     
46396     setActiveState : function(active){
46397         this.active = active;
46398         this.setActiveClass(active);
46399         
46400         if(!active){
46401             this.fireEvent("deactivate", this);
46402             return;
46403         }
46404         
46405         this.fireEvent("activate", this);
46406         // not sure if this should happen before or after..
46407         if (!this.layout) {
46408             return; // should not happen..
46409         }
46410         var reg = false;
46411         for (var r in this.layout.regions) {
46412             reg = this.layout.getRegion(r);
46413             if (reg.getActivePanel()) {
46414                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46415                 reg.setActivePanel(reg.getActivePanel());
46416                 continue;
46417             }
46418             if (!reg.panels.length) {
46419                 continue;
46420             }
46421             reg.showPanel(reg.getPanel(0));
46422         }
46423         
46424         
46425         
46426         
46427     },
46428     
46429     /**
46430      * Returns the nested BorderLayout for this panel
46431      * @return {Roo.BorderLayout} 
46432      */
46433     getLayout : function(){
46434         return this.layout;
46435     },
46436     
46437      /**
46438      * Adds a xtype elements to the layout of the nested panel
46439      * <pre><code>
46440
46441 panel.addxtype({
46442        xtype : 'ContentPanel',
46443        region: 'west',
46444        items: [ .... ]
46445    }
46446 );
46447
46448 panel.addxtype({
46449         xtype : 'NestedLayoutPanel',
46450         region: 'west',
46451         layout: {
46452            center: { },
46453            west: { }   
46454         },
46455         items : [ ... list of content panels or nested layout panels.. ]
46456    }
46457 );
46458 </code></pre>
46459      * @param {Object} cfg Xtype definition of item to add.
46460      */
46461     addxtype : function(cfg) {
46462         return this.layout.addxtype(cfg);
46463     
46464     }
46465 });/*
46466  * Based on:
46467  * Ext JS Library 1.1.1
46468  * Copyright(c) 2006-2007, Ext JS, LLC.
46469  *
46470  * Originally Released Under LGPL - original licence link has changed is not relivant.
46471  *
46472  * Fork - LGPL
46473  * <script type="text/javascript">
46474  */
46475 /**
46476  * @class Roo.TabPanel
46477  * @extends Roo.util.Observable
46478  * A lightweight tab container.
46479  * <br><br>
46480  * Usage:
46481  * <pre><code>
46482 // basic tabs 1, built from existing content
46483 var tabs = new Roo.TabPanel("tabs1");
46484 tabs.addTab("script", "View Script");
46485 tabs.addTab("markup", "View Markup");
46486 tabs.activate("script");
46487
46488 // more advanced tabs, built from javascript
46489 var jtabs = new Roo.TabPanel("jtabs");
46490 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46491
46492 // set up the UpdateManager
46493 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46494 var updater = tab2.getUpdateManager();
46495 updater.setDefaultUrl("ajax1.htm");
46496 tab2.on('activate', updater.refresh, updater, true);
46497
46498 // Use setUrl for Ajax loading
46499 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46500 tab3.setUrl("ajax2.htm", null, true);
46501
46502 // Disabled tab
46503 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46504 tab4.disable();
46505
46506 jtabs.activate("jtabs-1");
46507  * </code></pre>
46508  * @constructor
46509  * Create a new TabPanel.
46510  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46511  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46512  */
46513 Roo.bootstrap.panel.Tabs = function(config){
46514     /**
46515     * The container element for this TabPanel.
46516     * @type Roo.Element
46517     */
46518     this.el = Roo.get(config.el);
46519     delete config.el;
46520     if(config){
46521         if(typeof config == "boolean"){
46522             this.tabPosition = config ? "bottom" : "top";
46523         }else{
46524             Roo.apply(this, config);
46525         }
46526     }
46527     
46528     if(this.tabPosition == "bottom"){
46529         // if tabs are at the bottom = create the body first.
46530         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46531         this.el.addClass("roo-tabs-bottom");
46532     }
46533     // next create the tabs holders
46534     
46535     if (this.tabPosition == "west"){
46536         
46537         var reg = this.region; // fake it..
46538         while (reg) {
46539             if (!reg.mgr.parent) {
46540                 break;
46541             }
46542             reg = reg.mgr.parent.region;
46543         }
46544         Roo.log("got nest?");
46545         Roo.log(reg);
46546         if (reg.mgr.getRegion('west')) {
46547             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46548             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46549             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46550             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46551             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46552         
46553             
46554         }
46555         
46556         
46557     } else {
46558      
46559         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46560         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46561         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46562         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46563     }
46564     
46565     
46566     if(Roo.isIE){
46567         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46568     }
46569     
46570     // finally - if tabs are at the top, then create the body last..
46571     if(this.tabPosition != "bottom"){
46572         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46573          * @type Roo.Element
46574          */
46575         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46576         this.el.addClass("roo-tabs-top");
46577     }
46578     this.items = [];
46579
46580     this.bodyEl.setStyle("position", "relative");
46581
46582     this.active = null;
46583     this.activateDelegate = this.activate.createDelegate(this);
46584
46585     this.addEvents({
46586         /**
46587          * @event tabchange
46588          * Fires when the active tab changes
46589          * @param {Roo.TabPanel} this
46590          * @param {Roo.TabPanelItem} activePanel The new active tab
46591          */
46592         "tabchange": true,
46593         /**
46594          * @event beforetabchange
46595          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46596          * @param {Roo.TabPanel} this
46597          * @param {Object} e Set cancel to true on this object to cancel the tab change
46598          * @param {Roo.TabPanelItem} tab The tab being changed to
46599          */
46600         "beforetabchange" : true
46601     });
46602
46603     Roo.EventManager.onWindowResize(this.onResize, this);
46604     this.cpad = this.el.getPadding("lr");
46605     this.hiddenCount = 0;
46606
46607
46608     // toolbar on the tabbar support...
46609     if (this.toolbar) {
46610         alert("no toolbar support yet");
46611         this.toolbar  = false;
46612         /*
46613         var tcfg = this.toolbar;
46614         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
46615         this.toolbar = new Roo.Toolbar(tcfg);
46616         if (Roo.isSafari) {
46617             var tbl = tcfg.container.child('table', true);
46618             tbl.setAttribute('width', '100%');
46619         }
46620         */
46621         
46622     }
46623    
46624
46625
46626     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46627 };
46628
46629 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46630     /*
46631      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46632      */
46633     tabPosition : "top",
46634     /*
46635      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46636      */
46637     currentTabWidth : 0,
46638     /*
46639      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46640      */
46641     minTabWidth : 40,
46642     /*
46643      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46644      */
46645     maxTabWidth : 250,
46646     /*
46647      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46648      */
46649     preferredTabWidth : 175,
46650     /*
46651      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46652      */
46653     resizeTabs : false,
46654     /*
46655      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46656      */
46657     monitorResize : true,
46658     /*
46659      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
46660      */
46661     toolbar : false,  // set by caller..
46662     
46663     region : false, /// set by caller
46664     
46665     disableTooltips : true, // not used yet...
46666
46667     /**
46668      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46669      * @param {String} id The id of the div to use <b>or create</b>
46670      * @param {String} text The text for the tab
46671      * @param {String} content (optional) Content to put in the TabPanelItem body
46672      * @param {Boolean} closable (optional) True to create a close icon on the tab
46673      * @return {Roo.TabPanelItem} The created TabPanelItem
46674      */
46675     addTab : function(id, text, content, closable, tpl)
46676     {
46677         var item = new Roo.bootstrap.panel.TabItem({
46678             panel: this,
46679             id : id,
46680             text : text,
46681             closable : closable,
46682             tpl : tpl
46683         });
46684         this.addTabItem(item);
46685         if(content){
46686             item.setContent(content);
46687         }
46688         return item;
46689     },
46690
46691     /**
46692      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46693      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46694      * @return {Roo.TabPanelItem}
46695      */
46696     getTab : function(id){
46697         return this.items[id];
46698     },
46699
46700     /**
46701      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46702      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46703      */
46704     hideTab : function(id){
46705         var t = this.items[id];
46706         if(!t.isHidden()){
46707            t.setHidden(true);
46708            this.hiddenCount++;
46709            this.autoSizeTabs();
46710         }
46711     },
46712
46713     /**
46714      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46715      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46716      */
46717     unhideTab : function(id){
46718         var t = this.items[id];
46719         if(t.isHidden()){
46720            t.setHidden(false);
46721            this.hiddenCount--;
46722            this.autoSizeTabs();
46723         }
46724     },
46725
46726     /**
46727      * Adds an existing {@link Roo.TabPanelItem}.
46728      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46729      */
46730     addTabItem : function(item)
46731     {
46732         this.items[item.id] = item;
46733         this.items.push(item);
46734         this.autoSizeTabs();
46735       //  if(this.resizeTabs){
46736     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46737   //         this.autoSizeTabs();
46738 //        }else{
46739 //            item.autoSize();
46740        // }
46741     },
46742
46743     /**
46744      * Removes a {@link Roo.TabPanelItem}.
46745      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46746      */
46747     removeTab : function(id){
46748         var items = this.items;
46749         var tab = items[id];
46750         if(!tab) { return; }
46751         var index = items.indexOf(tab);
46752         if(this.active == tab && items.length > 1){
46753             var newTab = this.getNextAvailable(index);
46754             if(newTab) {
46755                 newTab.activate();
46756             }
46757         }
46758         this.stripEl.dom.removeChild(tab.pnode.dom);
46759         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46760             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46761         }
46762         items.splice(index, 1);
46763         delete this.items[tab.id];
46764         tab.fireEvent("close", tab);
46765         tab.purgeListeners();
46766         this.autoSizeTabs();
46767     },
46768
46769     getNextAvailable : function(start){
46770         var items = this.items;
46771         var index = start;
46772         // look for a next tab that will slide over to
46773         // replace the one being removed
46774         while(index < items.length){
46775             var item = items[++index];
46776             if(item && !item.isHidden()){
46777                 return item;
46778             }
46779         }
46780         // if one isn't found select the previous tab (on the left)
46781         index = start;
46782         while(index >= 0){
46783             var item = items[--index];
46784             if(item && !item.isHidden()){
46785                 return item;
46786             }
46787         }
46788         return null;
46789     },
46790
46791     /**
46792      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46793      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46794      */
46795     disableTab : function(id){
46796         var tab = this.items[id];
46797         if(tab && this.active != tab){
46798             tab.disable();
46799         }
46800     },
46801
46802     /**
46803      * Enables a {@link Roo.TabPanelItem} that is disabled.
46804      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46805      */
46806     enableTab : function(id){
46807         var tab = this.items[id];
46808         tab.enable();
46809     },
46810
46811     /**
46812      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46813      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46814      * @return {Roo.TabPanelItem} The TabPanelItem.
46815      */
46816     activate : function(id)
46817     {
46818         //Roo.log('activite:'  + id);
46819         
46820         var tab = this.items[id];
46821         if(!tab){
46822             return null;
46823         }
46824         if(tab == this.active || tab.disabled){
46825             return tab;
46826         }
46827         var e = {};
46828         this.fireEvent("beforetabchange", this, e, tab);
46829         if(e.cancel !== true && !tab.disabled){
46830             if(this.active){
46831                 this.active.hide();
46832             }
46833             this.active = this.items[id];
46834             this.active.show();
46835             this.fireEvent("tabchange", this, this.active);
46836         }
46837         return tab;
46838     },
46839
46840     /**
46841      * Gets the active {@link Roo.TabPanelItem}.
46842      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46843      */
46844     getActiveTab : function(){
46845         return this.active;
46846     },
46847
46848     /**
46849      * Updates the tab body element to fit the height of the container element
46850      * for overflow scrolling
46851      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46852      */
46853     syncHeight : function(targetHeight){
46854         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46855         var bm = this.bodyEl.getMargins();
46856         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46857         this.bodyEl.setHeight(newHeight);
46858         return newHeight;
46859     },
46860
46861     onResize : function(){
46862         if(this.monitorResize){
46863             this.autoSizeTabs();
46864         }
46865     },
46866
46867     /**
46868      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46869      */
46870     beginUpdate : function(){
46871         this.updating = true;
46872     },
46873
46874     /**
46875      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46876      */
46877     endUpdate : function(){
46878         this.updating = false;
46879         this.autoSizeTabs();
46880     },
46881
46882     /**
46883      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46884      */
46885     autoSizeTabs : function()
46886     {
46887         var count = this.items.length;
46888         var vcount = count - this.hiddenCount;
46889         
46890         if (vcount < 2) {
46891             this.stripEl.hide();
46892         } else {
46893             this.stripEl.show();
46894         }
46895         
46896         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46897             return;
46898         }
46899         
46900         
46901         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46902         var availWidth = Math.floor(w / vcount);
46903         var b = this.stripBody;
46904         if(b.getWidth() > w){
46905             var tabs = this.items;
46906             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46907             if(availWidth < this.minTabWidth){
46908                 /*if(!this.sleft){    // incomplete scrolling code
46909                     this.createScrollButtons();
46910                 }
46911                 this.showScroll();
46912                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46913             }
46914         }else{
46915             if(this.currentTabWidth < this.preferredTabWidth){
46916                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46917             }
46918         }
46919     },
46920
46921     /**
46922      * Returns the number of tabs in this TabPanel.
46923      * @return {Number}
46924      */
46925      getCount : function(){
46926          return this.items.length;
46927      },
46928
46929     /**
46930      * Resizes all the tabs to the passed width
46931      * @param {Number} The new width
46932      */
46933     setTabWidth : function(width){
46934         this.currentTabWidth = width;
46935         for(var i = 0, len = this.items.length; i < len; i++) {
46936                 if(!this.items[i].isHidden()) {
46937                 this.items[i].setWidth(width);
46938             }
46939         }
46940     },
46941
46942     /**
46943      * Destroys this TabPanel
46944      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46945      */
46946     destroy : function(removeEl){
46947         Roo.EventManager.removeResizeListener(this.onResize, this);
46948         for(var i = 0, len = this.items.length; i < len; i++){
46949             this.items[i].purgeListeners();
46950         }
46951         if(removeEl === true){
46952             this.el.update("");
46953             this.el.remove();
46954         }
46955     },
46956     
46957     createStrip : function(container)
46958     {
46959         var strip = document.createElement("nav");
46960         strip.className = Roo.bootstrap.version == 4 ?
46961             "navbar-light bg-light" : 
46962             "navbar navbar-default"; //"x-tabs-wrap";
46963         container.appendChild(strip);
46964         return strip;
46965     },
46966     
46967     createStripList : function(strip)
46968     {
46969         // div wrapper for retard IE
46970         // returns the "tr" element.
46971         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46972         //'<div class="x-tabs-strip-wrap">'+
46973           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46974           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46975         return strip.firstChild; //.firstChild.firstChild.firstChild;
46976     },
46977     createBody : function(container)
46978     {
46979         var body = document.createElement("div");
46980         Roo.id(body, "tab-body");
46981         //Roo.fly(body).addClass("x-tabs-body");
46982         Roo.fly(body).addClass("tab-content");
46983         container.appendChild(body);
46984         return body;
46985     },
46986     createItemBody :function(bodyEl, id){
46987         var body = Roo.getDom(id);
46988         if(!body){
46989             body = document.createElement("div");
46990             body.id = id;
46991         }
46992         //Roo.fly(body).addClass("x-tabs-item-body");
46993         Roo.fly(body).addClass("tab-pane");
46994          bodyEl.insertBefore(body, bodyEl.firstChild);
46995         return body;
46996     },
46997     /** @private */
46998     createStripElements :  function(stripEl, text, closable, tpl)
46999     {
47000         var td = document.createElement("li"); // was td..
47001         td.className = 'nav-item';
47002         
47003         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
47004         
47005         
47006         stripEl.appendChild(td);
47007         /*if(closable){
47008             td.className = "x-tabs-closable";
47009             if(!this.closeTpl){
47010                 this.closeTpl = new Roo.Template(
47011                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
47012                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
47013                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
47014                 );
47015             }
47016             var el = this.closeTpl.overwrite(td, {"text": text});
47017             var close = el.getElementsByTagName("div")[0];
47018             var inner = el.getElementsByTagName("em")[0];
47019             return {"el": el, "close": close, "inner": inner};
47020         } else {
47021         */
47022         // not sure what this is..
47023 //            if(!this.tabTpl){
47024                 //this.tabTpl = new Roo.Template(
47025                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
47026                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
47027                 //);
47028 //                this.tabTpl = new Roo.Template(
47029 //                   '<a href="#">' +
47030 //                   '<span unselectable="on"' +
47031 //                            (this.disableTooltips ? '' : ' title="{text}"') +
47032 //                            ' >{text}</span></a>'
47033 //                );
47034 //                
47035 //            }
47036
47037
47038             var template = tpl || this.tabTpl || false;
47039             
47040             if(!template){
47041                 template =  new Roo.Template(
47042                         Roo.bootstrap.version == 4 ? 
47043                             (
47044                                 '<a class="nav-link" href="#" unselectable="on"' +
47045                                      (this.disableTooltips ? '' : ' title="{text}"') +
47046                                      ' >{text}</a>'
47047                             ) : (
47048                                 '<a class="nav-link" href="#">' +
47049                                 '<span unselectable="on"' +
47050                                          (this.disableTooltips ? '' : ' title="{text}"') +
47051                                     ' >{text}</span></a>'
47052                             )
47053                 );
47054             }
47055             
47056             switch (typeof(template)) {
47057                 case 'object' :
47058                     break;
47059                 case 'string' :
47060                     template = new Roo.Template(template);
47061                     break;
47062                 default :
47063                     break;
47064             }
47065             
47066             var el = template.overwrite(td, {"text": text});
47067             
47068             var inner = el.getElementsByTagName("span")[0];
47069             
47070             return {"el": el, "inner": inner};
47071             
47072     }
47073         
47074     
47075 });
47076
47077 /**
47078  * @class Roo.TabPanelItem
47079  * @extends Roo.util.Observable
47080  * Represents an individual item (tab plus body) in a TabPanel.
47081  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
47082  * @param {String} id The id of this TabPanelItem
47083  * @param {String} text The text for the tab of this TabPanelItem
47084  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
47085  */
47086 Roo.bootstrap.panel.TabItem = function(config){
47087     /**
47088      * The {@link Roo.TabPanel} this TabPanelItem belongs to
47089      * @type Roo.TabPanel
47090      */
47091     this.tabPanel = config.panel;
47092     /**
47093      * The id for this TabPanelItem
47094      * @type String
47095      */
47096     this.id = config.id;
47097     /** @private */
47098     this.disabled = false;
47099     /** @private */
47100     this.text = config.text;
47101     /** @private */
47102     this.loaded = false;
47103     this.closable = config.closable;
47104
47105     /**
47106      * The body element for this TabPanelItem.
47107      * @type Roo.Element
47108      */
47109     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
47110     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
47111     this.bodyEl.setStyle("display", "block");
47112     this.bodyEl.setStyle("zoom", "1");
47113     //this.hideAction();
47114
47115     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
47116     /** @private */
47117     this.el = Roo.get(els.el);
47118     this.inner = Roo.get(els.inner, true);
47119      this.textEl = Roo.bootstrap.version == 4 ?
47120         this.el : Roo.get(this.el.dom.firstChild, true);
47121
47122     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
47123     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
47124
47125     
47126 //    this.el.on("mousedown", this.onTabMouseDown, this);
47127     this.el.on("click", this.onTabClick, this);
47128     /** @private */
47129     if(config.closable){
47130         var c = Roo.get(els.close, true);
47131         c.dom.title = this.closeText;
47132         c.addClassOnOver("close-over");
47133         c.on("click", this.closeClick, this);
47134      }
47135
47136     this.addEvents({
47137          /**
47138          * @event activate
47139          * Fires when this tab becomes the active tab.
47140          * @param {Roo.TabPanel} tabPanel The parent TabPanel
47141          * @param {Roo.TabPanelItem} this
47142          */
47143         "activate": true,
47144         /**
47145          * @event beforeclose
47146          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
47147          * @param {Roo.TabPanelItem} this
47148          * @param {Object} e Set cancel to true on this object to cancel the close.
47149          */
47150         "beforeclose": true,
47151         /**
47152          * @event close
47153          * Fires when this tab is closed.
47154          * @param {Roo.TabPanelItem} this
47155          */
47156          "close": true,
47157         /**
47158          * @event deactivate
47159          * Fires when this tab is no longer the active tab.
47160          * @param {Roo.TabPanel} tabPanel The parent TabPanel
47161          * @param {Roo.TabPanelItem} this
47162          */
47163          "deactivate" : true
47164     });
47165     this.hidden = false;
47166
47167     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
47168 };
47169
47170 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
47171            {
47172     purgeListeners : function(){
47173        Roo.util.Observable.prototype.purgeListeners.call(this);
47174        this.el.removeAllListeners();
47175     },
47176     /**
47177      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
47178      */
47179     show : function(){
47180         this.status_node.addClass("active");
47181         this.showAction();
47182         if(Roo.isOpera){
47183             this.tabPanel.stripWrap.repaint();
47184         }
47185         this.fireEvent("activate", this.tabPanel, this);
47186     },
47187
47188     /**
47189      * Returns true if this tab is the active tab.
47190      * @return {Boolean}
47191      */
47192     isActive : function(){
47193         return this.tabPanel.getActiveTab() == this;
47194     },
47195
47196     /**
47197      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
47198      */
47199     hide : function(){
47200         this.status_node.removeClass("active");
47201         this.hideAction();
47202         this.fireEvent("deactivate", this.tabPanel, this);
47203     },
47204
47205     hideAction : function(){
47206         this.bodyEl.hide();
47207         this.bodyEl.setStyle("position", "absolute");
47208         this.bodyEl.setLeft("-20000px");
47209         this.bodyEl.setTop("-20000px");
47210     },
47211
47212     showAction : function(){
47213         this.bodyEl.setStyle("position", "relative");
47214         this.bodyEl.setTop("");
47215         this.bodyEl.setLeft("");
47216         this.bodyEl.show();
47217     },
47218
47219     /**
47220      * Set the tooltip for the tab.
47221      * @param {String} tooltip The tab's tooltip
47222      */
47223     setTooltip : function(text){
47224         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
47225             this.textEl.dom.qtip = text;
47226             this.textEl.dom.removeAttribute('title');
47227         }else{
47228             this.textEl.dom.title = text;
47229         }
47230     },
47231
47232     onTabClick : function(e){
47233         e.preventDefault();
47234         this.tabPanel.activate(this.id);
47235     },
47236
47237     onTabMouseDown : function(e){
47238         e.preventDefault();
47239         this.tabPanel.activate(this.id);
47240     },
47241 /*
47242     getWidth : function(){
47243         return this.inner.getWidth();
47244     },
47245
47246     setWidth : function(width){
47247         var iwidth = width - this.linode.getPadding("lr");
47248         this.inner.setWidth(iwidth);
47249         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
47250         this.linode.setWidth(width);
47251     },
47252 */
47253     /**
47254      * Show or hide the tab
47255      * @param {Boolean} hidden True to hide or false to show.
47256      */
47257     setHidden : function(hidden){
47258         this.hidden = hidden;
47259         this.linode.setStyle("display", hidden ? "none" : "");
47260     },
47261
47262     /**
47263      * Returns true if this tab is "hidden"
47264      * @return {Boolean}
47265      */
47266     isHidden : function(){
47267         return this.hidden;
47268     },
47269
47270     /**
47271      * Returns the text for this tab
47272      * @return {String}
47273      */
47274     getText : function(){
47275         return this.text;
47276     },
47277     /*
47278     autoSize : function(){
47279         //this.el.beginMeasure();
47280         this.textEl.setWidth(1);
47281         /*
47282          *  #2804 [new] Tabs in Roojs
47283          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
47284          */
47285         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
47286         //this.el.endMeasure();
47287     //},
47288
47289     /**
47290      * Sets the text for the tab (Note: this also sets the tooltip text)
47291      * @param {String} text The tab's text and tooltip
47292      */
47293     setText : function(text){
47294         this.text = text;
47295         this.textEl.update(text);
47296         this.setTooltip(text);
47297         //if(!this.tabPanel.resizeTabs){
47298         //    this.autoSize();
47299         //}
47300     },
47301     /**
47302      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
47303      */
47304     activate : function(){
47305         this.tabPanel.activate(this.id);
47306     },
47307
47308     /**
47309      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
47310      */
47311     disable : function(){
47312         if(this.tabPanel.active != this){
47313             this.disabled = true;
47314             this.status_node.addClass("disabled");
47315         }
47316     },
47317
47318     /**
47319      * Enables this TabPanelItem if it was previously disabled.
47320      */
47321     enable : function(){
47322         this.disabled = false;
47323         this.status_node.removeClass("disabled");
47324     },
47325
47326     /**
47327      * Sets the content for this TabPanelItem.
47328      * @param {String} content The content
47329      * @param {Boolean} loadScripts true to look for and load scripts
47330      */
47331     setContent : function(content, loadScripts){
47332         this.bodyEl.update(content, loadScripts);
47333     },
47334
47335     /**
47336      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47337      * @return {Roo.UpdateManager} The UpdateManager
47338      */
47339     getUpdateManager : function(){
47340         return this.bodyEl.getUpdateManager();
47341     },
47342
47343     /**
47344      * Set a URL to be used to load the content for this TabPanelItem.
47345      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47346      * @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)
47347      * @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)
47348      * @return {Roo.UpdateManager} The UpdateManager
47349      */
47350     setUrl : function(url, params, loadOnce){
47351         if(this.refreshDelegate){
47352             this.un('activate', this.refreshDelegate);
47353         }
47354         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47355         this.on("activate", this.refreshDelegate);
47356         return this.bodyEl.getUpdateManager();
47357     },
47358
47359     /** @private */
47360     _handleRefresh : function(url, params, loadOnce){
47361         if(!loadOnce || !this.loaded){
47362             var updater = this.bodyEl.getUpdateManager();
47363             updater.update(url, params, this._setLoaded.createDelegate(this));
47364         }
47365     },
47366
47367     /**
47368      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
47369      *   Will fail silently if the setUrl method has not been called.
47370      *   This does not activate the panel, just updates its content.
47371      */
47372     refresh : function(){
47373         if(this.refreshDelegate){
47374            this.loaded = false;
47375            this.refreshDelegate();
47376         }
47377     },
47378
47379     /** @private */
47380     _setLoaded : function(){
47381         this.loaded = true;
47382     },
47383
47384     /** @private */
47385     closeClick : function(e){
47386         var o = {};
47387         e.stopEvent();
47388         this.fireEvent("beforeclose", this, o);
47389         if(o.cancel !== true){
47390             this.tabPanel.removeTab(this.id);
47391         }
47392     },
47393     /**
47394      * The text displayed in the tooltip for the close icon.
47395      * @type String
47396      */
47397     closeText : "Close this tab"
47398 });
47399 /**
47400 *    This script refer to:
47401 *    Title: International Telephone Input
47402 *    Author: Jack O'Connor
47403 *    Code version:  v12.1.12
47404 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47405 **/
47406
47407 Roo.bootstrap.form.PhoneInputData = function() {
47408     var d = [
47409       [
47410         "Afghanistan (‫افغانستان‬‎)",
47411         "af",
47412         "93"
47413       ],
47414       [
47415         "Albania (Shqipëri)",
47416         "al",
47417         "355"
47418       ],
47419       [
47420         "Algeria (‫الجزائر‬‎)",
47421         "dz",
47422         "213"
47423       ],
47424       [
47425         "American Samoa",
47426         "as",
47427         "1684"
47428       ],
47429       [
47430         "Andorra",
47431         "ad",
47432         "376"
47433       ],
47434       [
47435         "Angola",
47436         "ao",
47437         "244"
47438       ],
47439       [
47440         "Anguilla",
47441         "ai",
47442         "1264"
47443       ],
47444       [
47445         "Antigua and Barbuda",
47446         "ag",
47447         "1268"
47448       ],
47449       [
47450         "Argentina",
47451         "ar",
47452         "54"
47453       ],
47454       [
47455         "Armenia (Հայաստան)",
47456         "am",
47457         "374"
47458       ],
47459       [
47460         "Aruba",
47461         "aw",
47462         "297"
47463       ],
47464       [
47465         "Australia",
47466         "au",
47467         "61",
47468         0
47469       ],
47470       [
47471         "Austria (Österreich)",
47472         "at",
47473         "43"
47474       ],
47475       [
47476         "Azerbaijan (Azərbaycan)",
47477         "az",
47478         "994"
47479       ],
47480       [
47481         "Bahamas",
47482         "bs",
47483         "1242"
47484       ],
47485       [
47486         "Bahrain (‫البحرين‬‎)",
47487         "bh",
47488         "973"
47489       ],
47490       [
47491         "Bangladesh (বাংলাদেশ)",
47492         "bd",
47493         "880"
47494       ],
47495       [
47496         "Barbados",
47497         "bb",
47498         "1246"
47499       ],
47500       [
47501         "Belarus (Беларусь)",
47502         "by",
47503         "375"
47504       ],
47505       [
47506         "Belgium (België)",
47507         "be",
47508         "32"
47509       ],
47510       [
47511         "Belize",
47512         "bz",
47513         "501"
47514       ],
47515       [
47516         "Benin (Bénin)",
47517         "bj",
47518         "229"
47519       ],
47520       [
47521         "Bermuda",
47522         "bm",
47523         "1441"
47524       ],
47525       [
47526         "Bhutan (འབྲུག)",
47527         "bt",
47528         "975"
47529       ],
47530       [
47531         "Bolivia",
47532         "bo",
47533         "591"
47534       ],
47535       [
47536         "Bosnia and Herzegovina (Босна и Херцеговина)",
47537         "ba",
47538         "387"
47539       ],
47540       [
47541         "Botswana",
47542         "bw",
47543         "267"
47544       ],
47545       [
47546         "Brazil (Brasil)",
47547         "br",
47548         "55"
47549       ],
47550       [
47551         "British Indian Ocean Territory",
47552         "io",
47553         "246"
47554       ],
47555       [
47556         "British Virgin Islands",
47557         "vg",
47558         "1284"
47559       ],
47560       [
47561         "Brunei",
47562         "bn",
47563         "673"
47564       ],
47565       [
47566         "Bulgaria (България)",
47567         "bg",
47568         "359"
47569       ],
47570       [
47571         "Burkina Faso",
47572         "bf",
47573         "226"
47574       ],
47575       [
47576         "Burundi (Uburundi)",
47577         "bi",
47578         "257"
47579       ],
47580       [
47581         "Cambodia (កម្ពុជា)",
47582         "kh",
47583         "855"
47584       ],
47585       [
47586         "Cameroon (Cameroun)",
47587         "cm",
47588         "237"
47589       ],
47590       [
47591         "Canada",
47592         "ca",
47593         "1",
47594         1,
47595         ["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"]
47596       ],
47597       [
47598         "Cape Verde (Kabu Verdi)",
47599         "cv",
47600         "238"
47601       ],
47602       [
47603         "Caribbean Netherlands",
47604         "bq",
47605         "599",
47606         1
47607       ],
47608       [
47609         "Cayman Islands",
47610         "ky",
47611         "1345"
47612       ],
47613       [
47614         "Central African Republic (République centrafricaine)",
47615         "cf",
47616         "236"
47617       ],
47618       [
47619         "Chad (Tchad)",
47620         "td",
47621         "235"
47622       ],
47623       [
47624         "Chile",
47625         "cl",
47626         "56"
47627       ],
47628       [
47629         "China (中国)",
47630         "cn",
47631         "86"
47632       ],
47633       [
47634         "Christmas Island",
47635         "cx",
47636         "61",
47637         2
47638       ],
47639       [
47640         "Cocos (Keeling) Islands",
47641         "cc",
47642         "61",
47643         1
47644       ],
47645       [
47646         "Colombia",
47647         "co",
47648         "57"
47649       ],
47650       [
47651         "Comoros (‫جزر القمر‬‎)",
47652         "km",
47653         "269"
47654       ],
47655       [
47656         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47657         "cd",
47658         "243"
47659       ],
47660       [
47661         "Congo (Republic) (Congo-Brazzaville)",
47662         "cg",
47663         "242"
47664       ],
47665       [
47666         "Cook Islands",
47667         "ck",
47668         "682"
47669       ],
47670       [
47671         "Costa Rica",
47672         "cr",
47673         "506"
47674       ],
47675       [
47676         "Côte d’Ivoire",
47677         "ci",
47678         "225"
47679       ],
47680       [
47681         "Croatia (Hrvatska)",
47682         "hr",
47683         "385"
47684       ],
47685       [
47686         "Cuba",
47687         "cu",
47688         "53"
47689       ],
47690       [
47691         "Curaçao",
47692         "cw",
47693         "599",
47694         0
47695       ],
47696       [
47697         "Cyprus (Κύπρος)",
47698         "cy",
47699         "357"
47700       ],
47701       [
47702         "Czech Republic (Česká republika)",
47703         "cz",
47704         "420"
47705       ],
47706       [
47707         "Denmark (Danmark)",
47708         "dk",
47709         "45"
47710       ],
47711       [
47712         "Djibouti",
47713         "dj",
47714         "253"
47715       ],
47716       [
47717         "Dominica",
47718         "dm",
47719         "1767"
47720       ],
47721       [
47722         "Dominican Republic (República Dominicana)",
47723         "do",
47724         "1",
47725         2,
47726         ["809", "829", "849"]
47727       ],
47728       [
47729         "Ecuador",
47730         "ec",
47731         "593"
47732       ],
47733       [
47734         "Egypt (‫مصر‬‎)",
47735         "eg",
47736         "20"
47737       ],
47738       [
47739         "El Salvador",
47740         "sv",
47741         "503"
47742       ],
47743       [
47744         "Equatorial Guinea (Guinea Ecuatorial)",
47745         "gq",
47746         "240"
47747       ],
47748       [
47749         "Eritrea",
47750         "er",
47751         "291"
47752       ],
47753       [
47754         "Estonia (Eesti)",
47755         "ee",
47756         "372"
47757       ],
47758       [
47759         "Ethiopia",
47760         "et",
47761         "251"
47762       ],
47763       [
47764         "Falkland Islands (Islas Malvinas)",
47765         "fk",
47766         "500"
47767       ],
47768       [
47769         "Faroe Islands (Føroyar)",
47770         "fo",
47771         "298"
47772       ],
47773       [
47774         "Fiji",
47775         "fj",
47776         "679"
47777       ],
47778       [
47779         "Finland (Suomi)",
47780         "fi",
47781         "358",
47782         0
47783       ],
47784       [
47785         "France",
47786         "fr",
47787         "33"
47788       ],
47789       [
47790         "French Guiana (Guyane française)",
47791         "gf",
47792         "594"
47793       ],
47794       [
47795         "French Polynesia (Polynésie française)",
47796         "pf",
47797         "689"
47798       ],
47799       [
47800         "Gabon",
47801         "ga",
47802         "241"
47803       ],
47804       [
47805         "Gambia",
47806         "gm",
47807         "220"
47808       ],
47809       [
47810         "Georgia (საქართველო)",
47811         "ge",
47812         "995"
47813       ],
47814       [
47815         "Germany (Deutschland)",
47816         "de",
47817         "49"
47818       ],
47819       [
47820         "Ghana (Gaana)",
47821         "gh",
47822         "233"
47823       ],
47824       [
47825         "Gibraltar",
47826         "gi",
47827         "350"
47828       ],
47829       [
47830         "Greece (Ελλάδα)",
47831         "gr",
47832         "30"
47833       ],
47834       [
47835         "Greenland (Kalaallit Nunaat)",
47836         "gl",
47837         "299"
47838       ],
47839       [
47840         "Grenada",
47841         "gd",
47842         "1473"
47843       ],
47844       [
47845         "Guadeloupe",
47846         "gp",
47847         "590",
47848         0
47849       ],
47850       [
47851         "Guam",
47852         "gu",
47853         "1671"
47854       ],
47855       [
47856         "Guatemala",
47857         "gt",
47858         "502"
47859       ],
47860       [
47861         "Guernsey",
47862         "gg",
47863         "44",
47864         1
47865       ],
47866       [
47867         "Guinea (Guinée)",
47868         "gn",
47869         "224"
47870       ],
47871       [
47872         "Guinea-Bissau (Guiné Bissau)",
47873         "gw",
47874         "245"
47875       ],
47876       [
47877         "Guyana",
47878         "gy",
47879         "592"
47880       ],
47881       [
47882         "Haiti",
47883         "ht",
47884         "509"
47885       ],
47886       [
47887         "Honduras",
47888         "hn",
47889         "504"
47890       ],
47891       [
47892         "Hong Kong (香港)",
47893         "hk",
47894         "852"
47895       ],
47896       [
47897         "Hungary (Magyarország)",
47898         "hu",
47899         "36"
47900       ],
47901       [
47902         "Iceland (Ísland)",
47903         "is",
47904         "354"
47905       ],
47906       [
47907         "India (भारत)",
47908         "in",
47909         "91"
47910       ],
47911       [
47912         "Indonesia",
47913         "id",
47914         "62"
47915       ],
47916       [
47917         "Iran (‫ایران‬‎)",
47918         "ir",
47919         "98"
47920       ],
47921       [
47922         "Iraq (‫العراق‬‎)",
47923         "iq",
47924         "964"
47925       ],
47926       [
47927         "Ireland",
47928         "ie",
47929         "353"
47930       ],
47931       [
47932         "Isle of Man",
47933         "im",
47934         "44",
47935         2
47936       ],
47937       [
47938         "Israel (‫ישראל‬‎)",
47939         "il",
47940         "972"
47941       ],
47942       [
47943         "Italy (Italia)",
47944         "it",
47945         "39",
47946         0
47947       ],
47948       [
47949         "Jamaica",
47950         "jm",
47951         "1876"
47952       ],
47953       [
47954         "Japan (日本)",
47955         "jp",
47956         "81"
47957       ],
47958       [
47959         "Jersey",
47960         "je",
47961         "44",
47962         3
47963       ],
47964       [
47965         "Jordan (‫الأردن‬‎)",
47966         "jo",
47967         "962"
47968       ],
47969       [
47970         "Kazakhstan (Казахстан)",
47971         "kz",
47972         "7",
47973         1
47974       ],
47975       [
47976         "Kenya",
47977         "ke",
47978         "254"
47979       ],
47980       [
47981         "Kiribati",
47982         "ki",
47983         "686"
47984       ],
47985       [
47986         "Kosovo",
47987         "xk",
47988         "383"
47989       ],
47990       [
47991         "Kuwait (‫الكويت‬‎)",
47992         "kw",
47993         "965"
47994       ],
47995       [
47996         "Kyrgyzstan (Кыргызстан)",
47997         "kg",
47998         "996"
47999       ],
48000       [
48001         "Laos (ລາວ)",
48002         "la",
48003         "856"
48004       ],
48005       [
48006         "Latvia (Latvija)",
48007         "lv",
48008         "371"
48009       ],
48010       [
48011         "Lebanon (‫لبنان‬‎)",
48012         "lb",
48013         "961"
48014       ],
48015       [
48016         "Lesotho",
48017         "ls",
48018         "266"
48019       ],
48020       [
48021         "Liberia",
48022         "lr",
48023         "231"
48024       ],
48025       [
48026         "Libya (‫ليبيا‬‎)",
48027         "ly",
48028         "218"
48029       ],
48030       [
48031         "Liechtenstein",
48032         "li",
48033         "423"
48034       ],
48035       [
48036         "Lithuania (Lietuva)",
48037         "lt",
48038         "370"
48039       ],
48040       [
48041         "Luxembourg",
48042         "lu",
48043         "352"
48044       ],
48045       [
48046         "Macau (澳門)",
48047         "mo",
48048         "853"
48049       ],
48050       [
48051         "Macedonia (FYROM) (Македонија)",
48052         "mk",
48053         "389"
48054       ],
48055       [
48056         "Madagascar (Madagasikara)",
48057         "mg",
48058         "261"
48059       ],
48060       [
48061         "Malawi",
48062         "mw",
48063         "265"
48064       ],
48065       [
48066         "Malaysia",
48067         "my",
48068         "60"
48069       ],
48070       [
48071         "Maldives",
48072         "mv",
48073         "960"
48074       ],
48075       [
48076         "Mali",
48077         "ml",
48078         "223"
48079       ],
48080       [
48081         "Malta",
48082         "mt",
48083         "356"
48084       ],
48085       [
48086         "Marshall Islands",
48087         "mh",
48088         "692"
48089       ],
48090       [
48091         "Martinique",
48092         "mq",
48093         "596"
48094       ],
48095       [
48096         "Mauritania (‫موريتانيا‬‎)",
48097         "mr",
48098         "222"
48099       ],
48100       [
48101         "Mauritius (Moris)",
48102         "mu",
48103         "230"
48104       ],
48105       [
48106         "Mayotte",
48107         "yt",
48108         "262",
48109         1
48110       ],
48111       [
48112         "Mexico (México)",
48113         "mx",
48114         "52"
48115       ],
48116       [
48117         "Micronesia",
48118         "fm",
48119         "691"
48120       ],
48121       [
48122         "Moldova (Republica Moldova)",
48123         "md",
48124         "373"
48125       ],
48126       [
48127         "Monaco",
48128         "mc",
48129         "377"
48130       ],
48131       [
48132         "Mongolia (Монгол)",
48133         "mn",
48134         "976"
48135       ],
48136       [
48137         "Montenegro (Crna Gora)",
48138         "me",
48139         "382"
48140       ],
48141       [
48142         "Montserrat",
48143         "ms",
48144         "1664"
48145       ],
48146       [
48147         "Morocco (‫المغرب‬‎)",
48148         "ma",
48149         "212",
48150         0
48151       ],
48152       [
48153         "Mozambique (Moçambique)",
48154         "mz",
48155         "258"
48156       ],
48157       [
48158         "Myanmar (Burma) (မြန်မာ)",
48159         "mm",
48160         "95"
48161       ],
48162       [
48163         "Namibia (Namibië)",
48164         "na",
48165         "264"
48166       ],
48167       [
48168         "Nauru",
48169         "nr",
48170         "674"
48171       ],
48172       [
48173         "Nepal (नेपाल)",
48174         "np",
48175         "977"
48176       ],
48177       [
48178         "Netherlands (Nederland)",
48179         "nl",
48180         "31"
48181       ],
48182       [
48183         "New Caledonia (Nouvelle-Calédonie)",
48184         "nc",
48185         "687"
48186       ],
48187       [
48188         "New Zealand",
48189         "nz",
48190         "64"
48191       ],
48192       [
48193         "Nicaragua",
48194         "ni",
48195         "505"
48196       ],
48197       [
48198         "Niger (Nijar)",
48199         "ne",
48200         "227"
48201       ],
48202       [
48203         "Nigeria",
48204         "ng",
48205         "234"
48206       ],
48207       [
48208         "Niue",
48209         "nu",
48210         "683"
48211       ],
48212       [
48213         "Norfolk Island",
48214         "nf",
48215         "672"
48216       ],
48217       [
48218         "North Korea (조선 민주주의 인민 공화국)",
48219         "kp",
48220         "850"
48221       ],
48222       [
48223         "Northern Mariana Islands",
48224         "mp",
48225         "1670"
48226       ],
48227       [
48228         "Norway (Norge)",
48229         "no",
48230         "47",
48231         0
48232       ],
48233       [
48234         "Oman (‫عُمان‬‎)",
48235         "om",
48236         "968"
48237       ],
48238       [
48239         "Pakistan (‫پاکستان‬‎)",
48240         "pk",
48241         "92"
48242       ],
48243       [
48244         "Palau",
48245         "pw",
48246         "680"
48247       ],
48248       [
48249         "Palestine (‫فلسطين‬‎)",
48250         "ps",
48251         "970"
48252       ],
48253       [
48254         "Panama (Panamá)",
48255         "pa",
48256         "507"
48257       ],
48258       [
48259         "Papua New Guinea",
48260         "pg",
48261         "675"
48262       ],
48263       [
48264         "Paraguay",
48265         "py",
48266         "595"
48267       ],
48268       [
48269         "Peru (Perú)",
48270         "pe",
48271         "51"
48272       ],
48273       [
48274         "Philippines",
48275         "ph",
48276         "63"
48277       ],
48278       [
48279         "Poland (Polska)",
48280         "pl",
48281         "48"
48282       ],
48283       [
48284         "Portugal",
48285         "pt",
48286         "351"
48287       ],
48288       [
48289         "Puerto Rico",
48290         "pr",
48291         "1",
48292         3,
48293         ["787", "939"]
48294       ],
48295       [
48296         "Qatar (‫قطر‬‎)",
48297         "qa",
48298         "974"
48299       ],
48300       [
48301         "Réunion (La Réunion)",
48302         "re",
48303         "262",
48304         0
48305       ],
48306       [
48307         "Romania (România)",
48308         "ro",
48309         "40"
48310       ],
48311       [
48312         "Russia (Россия)",
48313         "ru",
48314         "7",
48315         0
48316       ],
48317       [
48318         "Rwanda",
48319         "rw",
48320         "250"
48321       ],
48322       [
48323         "Saint Barthélemy",
48324         "bl",
48325         "590",
48326         1
48327       ],
48328       [
48329         "Saint Helena",
48330         "sh",
48331         "290"
48332       ],
48333       [
48334         "Saint Kitts and Nevis",
48335         "kn",
48336         "1869"
48337       ],
48338       [
48339         "Saint Lucia",
48340         "lc",
48341         "1758"
48342       ],
48343       [
48344         "Saint Martin (Saint-Martin (partie française))",
48345         "mf",
48346         "590",
48347         2
48348       ],
48349       [
48350         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48351         "pm",
48352         "508"
48353       ],
48354       [
48355         "Saint Vincent and the Grenadines",
48356         "vc",
48357         "1784"
48358       ],
48359       [
48360         "Samoa",
48361         "ws",
48362         "685"
48363       ],
48364       [
48365         "San Marino",
48366         "sm",
48367         "378"
48368       ],
48369       [
48370         "São Tomé and Príncipe (São Tomé e Príncipe)",
48371         "st",
48372         "239"
48373       ],
48374       [
48375         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
48376         "sa",
48377         "966"
48378       ],
48379       [
48380         "Senegal (Sénégal)",
48381         "sn",
48382         "221"
48383       ],
48384       [
48385         "Serbia (Србија)",
48386         "rs",
48387         "381"
48388       ],
48389       [
48390         "Seychelles",
48391         "sc",
48392         "248"
48393       ],
48394       [
48395         "Sierra Leone",
48396         "sl",
48397         "232"
48398       ],
48399       [
48400         "Singapore",
48401         "sg",
48402         "65"
48403       ],
48404       [
48405         "Sint Maarten",
48406         "sx",
48407         "1721"
48408       ],
48409       [
48410         "Slovakia (Slovensko)",
48411         "sk",
48412         "421"
48413       ],
48414       [
48415         "Slovenia (Slovenija)",
48416         "si",
48417         "386"
48418       ],
48419       [
48420         "Solomon Islands",
48421         "sb",
48422         "677"
48423       ],
48424       [
48425         "Somalia (Soomaaliya)",
48426         "so",
48427         "252"
48428       ],
48429       [
48430         "South Africa",
48431         "za",
48432         "27"
48433       ],
48434       [
48435         "South Korea (대한민국)",
48436         "kr",
48437         "82"
48438       ],
48439       [
48440         "South Sudan (‫جنوب السودان‬‎)",
48441         "ss",
48442         "211"
48443       ],
48444       [
48445         "Spain (España)",
48446         "es",
48447         "34"
48448       ],
48449       [
48450         "Sri Lanka (ශ්‍රී ලංකාව)",
48451         "lk",
48452         "94"
48453       ],
48454       [
48455         "Sudan (‫السودان‬‎)",
48456         "sd",
48457         "249"
48458       ],
48459       [
48460         "Suriname",
48461         "sr",
48462         "597"
48463       ],
48464       [
48465         "Svalbard and Jan Mayen",
48466         "sj",
48467         "47",
48468         1
48469       ],
48470       [
48471         "Swaziland",
48472         "sz",
48473         "268"
48474       ],
48475       [
48476         "Sweden (Sverige)",
48477         "se",
48478         "46"
48479       ],
48480       [
48481         "Switzerland (Schweiz)",
48482         "ch",
48483         "41"
48484       ],
48485       [
48486         "Syria (‫سوريا‬‎)",
48487         "sy",
48488         "963"
48489       ],
48490       [
48491         "Taiwan (台灣)",
48492         "tw",
48493         "886"
48494       ],
48495       [
48496         "Tajikistan",
48497         "tj",
48498         "992"
48499       ],
48500       [
48501         "Tanzania",
48502         "tz",
48503         "255"
48504       ],
48505       [
48506         "Thailand (ไทย)",
48507         "th",
48508         "66"
48509       ],
48510       [
48511         "Timor-Leste",
48512         "tl",
48513         "670"
48514       ],
48515       [
48516         "Togo",
48517         "tg",
48518         "228"
48519       ],
48520       [
48521         "Tokelau",
48522         "tk",
48523         "690"
48524       ],
48525       [
48526         "Tonga",
48527         "to",
48528         "676"
48529       ],
48530       [
48531         "Trinidad and Tobago",
48532         "tt",
48533         "1868"
48534       ],
48535       [
48536         "Tunisia (‫تونس‬‎)",
48537         "tn",
48538         "216"
48539       ],
48540       [
48541         "Turkey (Türkiye)",
48542         "tr",
48543         "90"
48544       ],
48545       [
48546         "Turkmenistan",
48547         "tm",
48548         "993"
48549       ],
48550       [
48551         "Turks and Caicos Islands",
48552         "tc",
48553         "1649"
48554       ],
48555       [
48556         "Tuvalu",
48557         "tv",
48558         "688"
48559       ],
48560       [
48561         "U.S. Virgin Islands",
48562         "vi",
48563         "1340"
48564       ],
48565       [
48566         "Uganda",
48567         "ug",
48568         "256"
48569       ],
48570       [
48571         "Ukraine (Україна)",
48572         "ua",
48573         "380"
48574       ],
48575       [
48576         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
48577         "ae",
48578         "971"
48579       ],
48580       [
48581         "United Kingdom",
48582         "gb",
48583         "44",
48584         0
48585       ],
48586       [
48587         "United States",
48588         "us",
48589         "1",
48590         0
48591       ],
48592       [
48593         "Uruguay",
48594         "uy",
48595         "598"
48596       ],
48597       [
48598         "Uzbekistan (Oʻzbekiston)",
48599         "uz",
48600         "998"
48601       ],
48602       [
48603         "Vanuatu",
48604         "vu",
48605         "678"
48606       ],
48607       [
48608         "Vatican City (Città del Vaticano)",
48609         "va",
48610         "39",
48611         1
48612       ],
48613       [
48614         "Venezuela",
48615         "ve",
48616         "58"
48617       ],
48618       [
48619         "Vietnam (Việt Nam)",
48620         "vn",
48621         "84"
48622       ],
48623       [
48624         "Wallis and Futuna (Wallis-et-Futuna)",
48625         "wf",
48626         "681"
48627       ],
48628       [
48629         "Western Sahara (‫الصحراء الغربية‬‎)",
48630         "eh",
48631         "212",
48632         1
48633       ],
48634       [
48635         "Yemen (‫اليمن‬‎)",
48636         "ye",
48637         "967"
48638       ],
48639       [
48640         "Zambia",
48641         "zm",
48642         "260"
48643       ],
48644       [
48645         "Zimbabwe",
48646         "zw",
48647         "263"
48648       ],
48649       [
48650         "Åland Islands",
48651         "ax",
48652         "358",
48653         1
48654       ]
48655   ];
48656   
48657   return d;
48658 }/**
48659 *    This script refer to:
48660 *    Title: International Telephone Input
48661 *    Author: Jack O'Connor
48662 *    Code version:  v12.1.12
48663 *    Availability: https://github.com/jackocnr/intl-tel-input.git
48664 **/
48665
48666 /**
48667  * @class Roo.bootstrap.form.PhoneInput
48668  * @extends Roo.bootstrap.form.TriggerField
48669  * An input with International dial-code selection
48670  
48671  * @cfg {String} defaultDialCode default '+852'
48672  * @cfg {Array} preferedCountries default []
48673   
48674  * @constructor
48675  * Create a new PhoneInput.
48676  * @param {Object} config Configuration options
48677  */
48678
48679 Roo.bootstrap.form.PhoneInput = function(config) {
48680     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48681 };
48682
48683 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48684         /**
48685         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48686         */
48687         listWidth: undefined,
48688         
48689         selectedClass: 'active',
48690         
48691         invalidClass : "has-warning",
48692         
48693         validClass: 'has-success',
48694         
48695         allowed: '0123456789',
48696         
48697         max_length: 15,
48698         
48699         /**
48700          * @cfg {String} defaultDialCode The default dial code when initializing the input
48701          */
48702         defaultDialCode: '+852',
48703         
48704         /**
48705          * @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
48706          */
48707         preferedCountries: false,
48708         
48709         getAutoCreate : function()
48710         {
48711             var data = Roo.bootstrap.form.PhoneInputData();
48712             var align = this.labelAlign || this.parentLabelAlign();
48713             var id = Roo.id();
48714             
48715             this.allCountries = [];
48716             this.dialCodeMapping = [];
48717             
48718             for (var i = 0; i < data.length; i++) {
48719               var c = data[i];
48720               this.allCountries[i] = {
48721                 name: c[0],
48722                 iso2: c[1],
48723                 dialCode: c[2],
48724                 priority: c[3] || 0,
48725                 areaCodes: c[4] || null
48726               };
48727               this.dialCodeMapping[c[2]] = {
48728                   name: c[0],
48729                   iso2: c[1],
48730                   priority: c[3] || 0,
48731                   areaCodes: c[4] || null
48732               };
48733             }
48734             
48735             var cfg = {
48736                 cls: 'form-group',
48737                 cn: []
48738             };
48739             
48740             var input =  {
48741                 tag: 'input',
48742                 id : id,
48743                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48744                 maxlength: this.max_length,
48745                 cls : 'form-control tel-input',
48746                 autocomplete: 'new-password'
48747             };
48748             
48749             var hiddenInput = {
48750                 tag: 'input',
48751                 type: 'hidden',
48752                 cls: 'hidden-tel-input'
48753             };
48754             
48755             if (this.name) {
48756                 hiddenInput.name = this.name;
48757             }
48758             
48759             if (this.disabled) {
48760                 input.disabled = true;
48761             }
48762             
48763             var flag_container = {
48764                 tag: 'div',
48765                 cls: 'flag-box',
48766                 cn: [
48767                     {
48768                         tag: 'div',
48769                         cls: 'flag'
48770                     },
48771                     {
48772                         tag: 'div',
48773                         cls: 'caret'
48774                     }
48775                 ]
48776             };
48777             
48778             var box = {
48779                 tag: 'div',
48780                 cls: this.hasFeedback ? 'has-feedback' : '',
48781                 cn: [
48782                     hiddenInput,
48783                     input,
48784                     {
48785                         tag: 'input',
48786                         cls: 'dial-code-holder',
48787                         disabled: true
48788                     }
48789                 ]
48790             };
48791             
48792             var container = {
48793                 cls: 'roo-select2-container input-group',
48794                 cn: [
48795                     flag_container,
48796                     box
48797                 ]
48798             };
48799             
48800             if (this.fieldLabel.length) {
48801                 var indicator = {
48802                     tag: 'i',
48803                     tooltip: 'This field is required'
48804                 };
48805                 
48806                 var label = {
48807                     tag: 'label',
48808                     'for':  id,
48809                     cls: 'control-label',
48810                     cn: []
48811                 };
48812                 
48813                 var label_text = {
48814                     tag: 'span',
48815                     html: this.fieldLabel
48816                 };
48817                 
48818                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48819                 label.cn = [
48820                     indicator,
48821                     label_text
48822                 ];
48823                 
48824                 if(this.indicatorpos == 'right') {
48825                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48826                     label.cn = [
48827                         label_text,
48828                         indicator
48829                     ];
48830                 }
48831                 
48832                 if(align == 'left') {
48833                     container = {
48834                         tag: 'div',
48835                         cn: [
48836                             container
48837                         ]
48838                     };
48839                     
48840                     if(this.labelWidth > 12){
48841                         label.style = "width: " + this.labelWidth + 'px';
48842                     }
48843                     if(this.labelWidth < 13 && this.labelmd == 0){
48844                         this.labelmd = this.labelWidth;
48845                     }
48846                     if(this.labellg > 0){
48847                         label.cls += ' col-lg-' + this.labellg;
48848                         input.cls += ' col-lg-' + (12 - this.labellg);
48849                     }
48850                     if(this.labelmd > 0){
48851                         label.cls += ' col-md-' + this.labelmd;
48852                         container.cls += ' col-md-' + (12 - this.labelmd);
48853                     }
48854                     if(this.labelsm > 0){
48855                         label.cls += ' col-sm-' + this.labelsm;
48856                         container.cls += ' col-sm-' + (12 - this.labelsm);
48857                     }
48858                     if(this.labelxs > 0){
48859                         label.cls += ' col-xs-' + this.labelxs;
48860                         container.cls += ' col-xs-' + (12 - this.labelxs);
48861                     }
48862                 }
48863             }
48864             
48865             cfg.cn = [
48866                 label,
48867                 container
48868             ];
48869             
48870             var settings = this;
48871             
48872             ['xs','sm','md','lg'].map(function(size){
48873                 if (settings[size]) {
48874                     cfg.cls += ' col-' + size + '-' + settings[size];
48875                 }
48876             });
48877             
48878             this.store = new Roo.data.Store({
48879                 proxy : new Roo.data.MemoryProxy({}),
48880                 reader : new Roo.data.JsonReader({
48881                     fields : [
48882                         {
48883                             'name' : 'name',
48884                             'type' : 'string'
48885                         },
48886                         {
48887                             'name' : 'iso2',
48888                             'type' : 'string'
48889                         },
48890                         {
48891                             'name' : 'dialCode',
48892                             'type' : 'string'
48893                         },
48894                         {
48895                             'name' : 'priority',
48896                             'type' : 'string'
48897                         },
48898                         {
48899                             'name' : 'areaCodes',
48900                             'type' : 'string'
48901                         }
48902                     ]
48903                 })
48904             });
48905             
48906             if(!this.preferedCountries) {
48907                 this.preferedCountries = [
48908                     'hk',
48909                     'gb',
48910                     'us'
48911                 ];
48912             }
48913             
48914             var p = this.preferedCountries.reverse();
48915             
48916             if(p) {
48917                 for (var i = 0; i < p.length; i++) {
48918                     for (var j = 0; j < this.allCountries.length; j++) {
48919                         if(this.allCountries[j].iso2 == p[i]) {
48920                             var t = this.allCountries[j];
48921                             this.allCountries.splice(j,1);
48922                             this.allCountries.unshift(t);
48923                         }
48924                     } 
48925                 }
48926             }
48927             
48928             this.store.proxy.data = {
48929                 success: true,
48930                 data: this.allCountries
48931             };
48932             
48933             return cfg;
48934         },
48935         
48936         initEvents : function()
48937         {
48938             this.createList();
48939             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48940             
48941             this.indicator = this.indicatorEl();
48942             this.flag = this.flagEl();
48943             this.dialCodeHolder = this.dialCodeHolderEl();
48944             
48945             this.trigger = this.el.select('div.flag-box',true).first();
48946             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48947             
48948             var _this = this;
48949             
48950             (function(){
48951                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48952                 _this.list.setWidth(lw);
48953             }).defer(100);
48954             
48955             this.list.on('mouseover', this.onViewOver, this);
48956             this.list.on('mousemove', this.onViewMove, this);
48957             this.inputEl().on("keyup", this.onKeyUp, this);
48958             this.inputEl().on("keypress", this.onKeyPress, this);
48959             
48960             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48961
48962             this.view = new Roo.View(this.list, this.tpl, {
48963                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48964             });
48965             
48966             this.view.on('click', this.onViewClick, this);
48967             this.setValue(this.defaultDialCode);
48968         },
48969         
48970         onTriggerClick : function(e)
48971         {
48972             Roo.log('trigger click');
48973             if(this.disabled){
48974                 return;
48975             }
48976             
48977             if(this.isExpanded()){
48978                 this.collapse();
48979                 this.hasFocus = false;
48980             }else {
48981                 this.store.load({});
48982                 this.hasFocus = true;
48983                 this.expand();
48984             }
48985         },
48986         
48987         isExpanded : function()
48988         {
48989             return this.list.isVisible();
48990         },
48991         
48992         collapse : function()
48993         {
48994             if(!this.isExpanded()){
48995                 return;
48996             }
48997             this.list.hide();
48998             Roo.get(document).un('mousedown', this.collapseIf, this);
48999             Roo.get(document).un('mousewheel', this.collapseIf, this);
49000             this.fireEvent('collapse', this);
49001             this.validate();
49002         },
49003         
49004         expand : function()
49005         {
49006             Roo.log('expand');
49007
49008             if(this.isExpanded() || !this.hasFocus){
49009                 return;
49010             }
49011             
49012             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
49013             this.list.setWidth(lw);
49014             
49015             this.list.show();
49016             this.restrictHeight();
49017             
49018             Roo.get(document).on('mousedown', this.collapseIf, this);
49019             Roo.get(document).on('mousewheel', this.collapseIf, this);
49020             
49021             this.fireEvent('expand', this);
49022         },
49023         
49024         restrictHeight : function()
49025         {
49026             this.list.alignTo(this.inputEl(), this.listAlign);
49027             this.list.alignTo(this.inputEl(), this.listAlign);
49028         },
49029         
49030         onViewOver : function(e, t)
49031         {
49032             if(this.inKeyMode){
49033                 return;
49034             }
49035             var item = this.view.findItemFromChild(t);
49036             
49037             if(item){
49038                 var index = this.view.indexOf(item);
49039                 this.select(index, false);
49040             }
49041         },
49042
49043         // private
49044         onViewClick : function(view, doFocus, el, e)
49045         {
49046             var index = this.view.getSelectedIndexes()[0];
49047             
49048             var r = this.store.getAt(index);
49049             
49050             if(r){
49051                 this.onSelect(r, index);
49052             }
49053             if(doFocus !== false && !this.blockFocus){
49054                 this.inputEl().focus();
49055             }
49056         },
49057         
49058         onViewMove : function(e, t)
49059         {
49060             this.inKeyMode = false;
49061         },
49062         
49063         select : function(index, scrollIntoView)
49064         {
49065             this.selectedIndex = index;
49066             this.view.select(index);
49067             if(scrollIntoView !== false){
49068                 var el = this.view.getNode(index);
49069                 if(el){
49070                     this.list.scrollChildIntoView(el, false);
49071                 }
49072             }
49073         },
49074         
49075         createList : function()
49076         {
49077             this.list = Roo.get(document.body).createChild({
49078                 tag: 'ul',
49079                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
49080                 style: 'display:none'
49081             });
49082             
49083             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
49084         },
49085         
49086         collapseIf : function(e)
49087         {
49088             var in_combo  = e.within(this.el);
49089             var in_list =  e.within(this.list);
49090             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
49091             
49092             if (in_combo || in_list || is_list) {
49093                 return;
49094             }
49095             this.collapse();
49096         },
49097         
49098         onSelect : function(record, index)
49099         {
49100             if(this.fireEvent('beforeselect', this, record, index) !== false){
49101                 
49102                 this.setFlagClass(record.data.iso2);
49103                 this.setDialCode(record.data.dialCode);
49104                 this.hasFocus = false;
49105                 this.collapse();
49106                 this.fireEvent('select', this, record, index);
49107             }
49108         },
49109         
49110         flagEl : function()
49111         {
49112             var flag = this.el.select('div.flag',true).first();
49113             if(!flag){
49114                 return false;
49115             }
49116             return flag;
49117         },
49118         
49119         dialCodeHolderEl : function()
49120         {
49121             var d = this.el.select('input.dial-code-holder',true).first();
49122             if(!d){
49123                 return false;
49124             }
49125             return d;
49126         },
49127         
49128         setDialCode : function(v)
49129         {
49130             this.dialCodeHolder.dom.value = '+'+v;
49131         },
49132         
49133         setFlagClass : function(n)
49134         {
49135             this.flag.dom.className = 'flag '+n;
49136         },
49137         
49138         getValue : function()
49139         {
49140             var v = this.inputEl().getValue();
49141             if(this.dialCodeHolder) {
49142                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
49143             }
49144             return v;
49145         },
49146         
49147         setValue : function(v)
49148         {
49149             var d = this.getDialCode(v);
49150             
49151             //invalid dial code
49152             if(v.length == 0 || !d || d.length == 0) {
49153                 if(this.rendered){
49154                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
49155                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49156                 }
49157                 return;
49158             }
49159             
49160             //valid dial code
49161             this.setFlagClass(this.dialCodeMapping[d].iso2);
49162             this.setDialCode(d);
49163             this.inputEl().dom.value = v.replace('+'+d,'');
49164             this.hiddenEl().dom.value = this.getValue();
49165             
49166             this.validate();
49167         },
49168         
49169         getDialCode : function(v)
49170         {
49171             v = v ||  '';
49172             
49173             if (v.length == 0) {
49174                 return this.dialCodeHolder.dom.value;
49175             }
49176             
49177             var dialCode = "";
49178             if (v.charAt(0) != "+") {
49179                 return false;
49180             }
49181             var numericChars = "";
49182             for (var i = 1; i < v.length; i++) {
49183               var c = v.charAt(i);
49184               if (!isNaN(c)) {
49185                 numericChars += c;
49186                 if (this.dialCodeMapping[numericChars]) {
49187                   dialCode = v.substr(1, i);
49188                 }
49189                 if (numericChars.length == 4) {
49190                   break;
49191                 }
49192               }
49193             }
49194             return dialCode;
49195         },
49196         
49197         reset : function()
49198         {
49199             this.setValue(this.defaultDialCode);
49200             this.validate();
49201         },
49202         
49203         hiddenEl : function()
49204         {
49205             return this.el.select('input.hidden-tel-input',true).first();
49206         },
49207         
49208         // after setting val
49209         onKeyUp : function(e){
49210             this.setValue(this.getValue());
49211         },
49212         
49213         onKeyPress : function(e){
49214             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
49215                 e.stopEvent();
49216             }
49217         }
49218         
49219 });
49220 /**
49221  * @class Roo.bootstrap.form.MoneyField
49222  * @extends Roo.bootstrap.form.ComboBox
49223  * Bootstrap MoneyField class
49224  * 
49225  * @constructor
49226  * Create a new MoneyField.
49227  * @param {Object} config Configuration options
49228  */
49229
49230 Roo.bootstrap.form.MoneyField = function(config) {
49231     
49232     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
49233     
49234 };
49235
49236 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
49237     
49238     /**
49239      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
49240      */
49241     allowDecimals : true,
49242     /**
49243      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
49244      */
49245     decimalSeparator : ".",
49246     /**
49247      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
49248      */
49249     decimalPrecision : 0,
49250     /**
49251      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
49252      */
49253     allowNegative : true,
49254     /**
49255      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
49256      */
49257     allowZero: true,
49258     /**
49259      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
49260      */
49261     minValue : Number.NEGATIVE_INFINITY,
49262     /**
49263      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
49264      */
49265     maxValue : Number.MAX_VALUE,
49266     /**
49267      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
49268      */
49269     minText : "The minimum value for this field is {0}",
49270     /**
49271      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
49272      */
49273     maxText : "The maximum value for this field is {0}",
49274     /**
49275      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
49276      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
49277      */
49278     nanText : "{0} is not a valid number",
49279     /**
49280      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
49281      */
49282     castInt : true,
49283     /**
49284      * @cfg {String} defaults currency of the MoneyField
49285      * value should be in lkey
49286      */
49287     defaultCurrency : false,
49288     /**
49289      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
49290      */
49291     thousandsDelimiter : false,
49292     /**
49293      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
49294      */
49295     max_length: false,
49296     
49297     inputlg : 9,
49298     inputmd : 9,
49299     inputsm : 9,
49300     inputxs : 6,
49301      /**
49302      * @cfg {Roo.data.Store} store  Store to lookup currency??
49303      */
49304     store : false,
49305     
49306     getAutoCreate : function()
49307     {
49308         var align = this.labelAlign || this.parentLabelAlign();
49309         
49310         var id = Roo.id();
49311
49312         var cfg = {
49313             cls: 'form-group',
49314             cn: []
49315         };
49316
49317         var input =  {
49318             tag: 'input',
49319             id : id,
49320             cls : 'form-control roo-money-amount-input',
49321             autocomplete: 'new-password'
49322         };
49323         
49324         var hiddenInput = {
49325             tag: 'input',
49326             type: 'hidden',
49327             id: Roo.id(),
49328             cls: 'hidden-number-input'
49329         };
49330         
49331         if(this.max_length) {
49332             input.maxlength = this.max_length; 
49333         }
49334         
49335         if (this.name) {
49336             hiddenInput.name = this.name;
49337         }
49338
49339         if (this.disabled) {
49340             input.disabled = true;
49341         }
49342
49343         var clg = 12 - this.inputlg;
49344         var cmd = 12 - this.inputmd;
49345         var csm = 12 - this.inputsm;
49346         var cxs = 12 - this.inputxs;
49347         
49348         var container = {
49349             tag : 'div',
49350             cls : 'row roo-money-field',
49351             cn : [
49352                 {
49353                     tag : 'div',
49354                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49355                     cn : [
49356                         {
49357                             tag : 'div',
49358                             cls: 'roo-select2-container input-group',
49359                             cn: [
49360                                 {
49361                                     tag : 'input',
49362                                     cls : 'form-control roo-money-currency-input',
49363                                     autocomplete: 'new-password',
49364                                     readOnly : 1,
49365                                     name : this.currencyName
49366                                 },
49367                                 {
49368                                     tag :'span',
49369                                     cls : 'input-group-addon',
49370                                     cn : [
49371                                         {
49372                                             tag: 'span',
49373                                             cls: 'caret'
49374                                         }
49375                                     ]
49376                                 }
49377                             ]
49378                         }
49379                     ]
49380                 },
49381                 {
49382                     tag : 'div',
49383                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49384                     cn : [
49385                         {
49386                             tag: 'div',
49387                             cls: this.hasFeedback ? 'has-feedback' : '',
49388                             cn: [
49389                                 input
49390                             ]
49391                         }
49392                     ]
49393                 }
49394             ]
49395             
49396         };
49397         
49398         if (this.fieldLabel.length) {
49399             var indicator = {
49400                 tag: 'i',
49401                 tooltip: 'This field is required'
49402             };
49403
49404             var label = {
49405                 tag: 'label',
49406                 'for':  id,
49407                 cls: 'control-label',
49408                 cn: []
49409             };
49410
49411             var label_text = {
49412                 tag: 'span',
49413                 html: this.fieldLabel
49414             };
49415
49416             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49417             label.cn = [
49418                 indicator,
49419                 label_text
49420             ];
49421
49422             if(this.indicatorpos == 'right') {
49423                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49424                 label.cn = [
49425                     label_text,
49426                     indicator
49427                 ];
49428             }
49429
49430             if(align == 'left') {
49431                 container = {
49432                     tag: 'div',
49433                     cn: [
49434                         container
49435                     ]
49436                 };
49437
49438                 if(this.labelWidth > 12){
49439                     label.style = "width: " + this.labelWidth + 'px';
49440                 }
49441                 if(this.labelWidth < 13 && this.labelmd == 0){
49442                     this.labelmd = this.labelWidth;
49443                 }
49444                 if(this.labellg > 0){
49445                     label.cls += ' col-lg-' + this.labellg;
49446                     input.cls += ' col-lg-' + (12 - this.labellg);
49447                 }
49448                 if(this.labelmd > 0){
49449                     label.cls += ' col-md-' + this.labelmd;
49450                     container.cls += ' col-md-' + (12 - this.labelmd);
49451                 }
49452                 if(this.labelsm > 0){
49453                     label.cls += ' col-sm-' + this.labelsm;
49454                     container.cls += ' col-sm-' + (12 - this.labelsm);
49455                 }
49456                 if(this.labelxs > 0){
49457                     label.cls += ' col-xs-' + this.labelxs;
49458                     container.cls += ' col-xs-' + (12 - this.labelxs);
49459                 }
49460             }
49461         }
49462
49463         cfg.cn = [
49464             label,
49465             container,
49466             hiddenInput
49467         ];
49468         
49469         var settings = this;
49470
49471         ['xs','sm','md','lg'].map(function(size){
49472             if (settings[size]) {
49473                 cfg.cls += ' col-' + size + '-' + settings[size];
49474             }
49475         });
49476         
49477         return cfg;
49478     },
49479     
49480     initEvents : function()
49481     {
49482         this.indicator = this.indicatorEl();
49483         
49484         this.initCurrencyEvent();
49485         
49486         this.initNumberEvent();
49487     },
49488     
49489     initCurrencyEvent : function()
49490     {
49491         if (!this.store) {
49492             throw "can not find store for combo";
49493         }
49494         
49495         this.store = Roo.factory(this.store, Roo.data);
49496         this.store.parent = this;
49497         
49498         this.createList();
49499         
49500         this.triggerEl = this.el.select('.input-group-addon', true).first();
49501         
49502         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49503         
49504         var _this = this;
49505         
49506         (function(){
49507             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49508             _this.list.setWidth(lw);
49509         }).defer(100);
49510         
49511         this.list.on('mouseover', this.onViewOver, this);
49512         this.list.on('mousemove', this.onViewMove, this);
49513         this.list.on('scroll', this.onViewScroll, this);
49514         
49515         if(!this.tpl){
49516             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49517         }
49518         
49519         this.view = new Roo.View(this.list, this.tpl, {
49520             singleSelect:true, store: this.store, selectedClass: this.selectedClass
49521         });
49522         
49523         this.view.on('click', this.onViewClick, this);
49524         
49525         this.store.on('beforeload', this.onBeforeLoad, this);
49526         this.store.on('load', this.onLoad, this);
49527         this.store.on('loadexception', this.onLoadException, this);
49528         
49529         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49530             "up" : function(e){
49531                 this.inKeyMode = true;
49532                 this.selectPrev();
49533             },
49534
49535             "down" : function(e){
49536                 if(!this.isExpanded()){
49537                     this.onTriggerClick();
49538                 }else{
49539                     this.inKeyMode = true;
49540                     this.selectNext();
49541                 }
49542             },
49543
49544             "enter" : function(e){
49545                 this.collapse();
49546                 
49547                 if(this.fireEvent("specialkey", this, e)){
49548                     this.onViewClick(false);
49549                 }
49550                 
49551                 return true;
49552             },
49553
49554             "esc" : function(e){
49555                 this.collapse();
49556             },
49557
49558             "tab" : function(e){
49559                 this.collapse();
49560                 
49561                 if(this.fireEvent("specialkey", this, e)){
49562                     this.onViewClick(false);
49563                 }
49564                 
49565                 return true;
49566             },
49567
49568             scope : this,
49569
49570             doRelay : function(foo, bar, hname){
49571                 if(hname == 'down' || this.scope.isExpanded()){
49572                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49573                 }
49574                 return true;
49575             },
49576
49577             forceKeyDown: true
49578         });
49579         
49580         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49581         
49582     },
49583     
49584     initNumberEvent : function(e)
49585     {
49586         this.inputEl().on("keydown" , this.fireKey,  this);
49587         this.inputEl().on("focus", this.onFocus,  this);
49588         this.inputEl().on("blur", this.onBlur,  this);
49589         
49590         this.inputEl().relayEvent('keyup', this);
49591         
49592         if(this.indicator){
49593             this.indicator.addClass('invisible');
49594         }
49595  
49596         this.originalValue = this.getValue();
49597         
49598         if(this.validationEvent == 'keyup'){
49599             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49600             this.inputEl().on('keyup', this.filterValidation, this);
49601         }
49602         else if(this.validationEvent !== false){
49603             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49604         }
49605         
49606         if(this.selectOnFocus){
49607             this.on("focus", this.preFocus, this);
49608             
49609         }
49610         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49611             this.inputEl().on("keypress", this.filterKeys, this);
49612         } else {
49613             this.inputEl().relayEvent('keypress', this);
49614         }
49615         
49616         var allowed = "0123456789";
49617         
49618         if(this.allowDecimals){
49619             allowed += this.decimalSeparator;
49620         }
49621         
49622         if(this.allowNegative){
49623             allowed += "-";
49624         }
49625         
49626         if(this.thousandsDelimiter) {
49627             allowed += ",";
49628         }
49629         
49630         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49631         
49632         var keyPress = function(e){
49633             
49634             var k = e.getKey();
49635             
49636             var c = e.getCharCode();
49637             
49638             if(
49639                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49640                     allowed.indexOf(String.fromCharCode(c)) === -1
49641             ){
49642                 e.stopEvent();
49643                 return;
49644             }
49645             
49646             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49647                 return;
49648             }
49649             
49650             if(allowed.indexOf(String.fromCharCode(c)) === -1){
49651                 e.stopEvent();
49652             }
49653         };
49654         
49655         this.inputEl().on("keypress", keyPress, this);
49656         
49657     },
49658     
49659     onTriggerClick : function(e)
49660     {   
49661         if(this.disabled){
49662             return;
49663         }
49664         
49665         this.page = 0;
49666         this.loadNext = false;
49667         
49668         if(this.isExpanded()){
49669             this.collapse();
49670             return;
49671         }
49672         
49673         this.hasFocus = true;
49674         
49675         if(this.triggerAction == 'all') {
49676             this.doQuery(this.allQuery, true);
49677             return;
49678         }
49679         
49680         this.doQuery(this.getRawValue());
49681     },
49682     
49683     getCurrency : function()
49684     {   
49685         var v = this.currencyEl().getValue();
49686         
49687         return v;
49688     },
49689     
49690     restrictHeight : function()
49691     {
49692         this.list.alignTo(this.currencyEl(), this.listAlign);
49693         this.list.alignTo(this.currencyEl(), this.listAlign);
49694     },
49695     
49696     onViewClick : function(view, doFocus, el, e)
49697     {
49698         var index = this.view.getSelectedIndexes()[0];
49699         
49700         var r = this.store.getAt(index);
49701         
49702         if(r){
49703             this.onSelect(r, index);
49704         }
49705     },
49706     
49707     onSelect : function(record, index){
49708         
49709         if(this.fireEvent('beforeselect', this, record, index) !== false){
49710         
49711             this.setFromCurrencyData(index > -1 ? record.data : false);
49712             
49713             this.collapse();
49714             
49715             this.fireEvent('select', this, record, index);
49716         }
49717     },
49718     
49719     setFromCurrencyData : function(o)
49720     {
49721         var currency = '';
49722         
49723         this.lastCurrency = o;
49724         
49725         if (this.currencyField) {
49726             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49727         } else {
49728             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49729         }
49730         
49731         this.lastSelectionText = currency;
49732         
49733         //setting default currency
49734         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49735             this.setCurrency(this.defaultCurrency);
49736             return;
49737         }
49738         
49739         this.setCurrency(currency);
49740     },
49741     
49742     setFromData : function(o)
49743     {
49744         var c = {};
49745         
49746         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49747         
49748         this.setFromCurrencyData(c);
49749         
49750         var value = '';
49751         
49752         if (this.name) {
49753             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49754         } else {
49755             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49756         }
49757         
49758         this.setValue(value);
49759         
49760     },
49761     
49762     setCurrency : function(v)
49763     {   
49764         this.currencyValue = v;
49765         
49766         if(this.rendered){
49767             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49768             this.validate();
49769         }
49770     },
49771     
49772     setValue : function(v)
49773     {
49774         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49775         
49776         this.value = v;
49777         
49778         if(this.rendered){
49779             
49780             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49781             
49782             this.inputEl().dom.value = (v == '') ? '' :
49783                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49784             
49785             if(!this.allowZero && v === '0') {
49786                 this.hiddenEl().dom.value = '';
49787                 this.inputEl().dom.value = '';
49788             }
49789             
49790             this.validate();
49791         }
49792     },
49793     
49794     getRawValue : function()
49795     {
49796         var v = this.inputEl().getValue();
49797         
49798         return v;
49799     },
49800     
49801     getValue : function()
49802     {
49803         return this.fixPrecision(this.parseValue(this.getRawValue()));
49804     },
49805     
49806     parseValue : function(value)
49807     {
49808         if(this.thousandsDelimiter) {
49809             value += "";
49810             r = new RegExp(",", "g");
49811             value = value.replace(r, "");
49812         }
49813         
49814         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49815         return isNaN(value) ? '' : value;
49816         
49817     },
49818     
49819     fixPrecision : function(value)
49820     {
49821         if(this.thousandsDelimiter) {
49822             value += "";
49823             r = new RegExp(",", "g");
49824             value = value.replace(r, "");
49825         }
49826         
49827         var nan = isNaN(value);
49828         
49829         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49830             return nan ? '' : value;
49831         }
49832         return parseFloat(value).toFixed(this.decimalPrecision);
49833     },
49834     
49835     decimalPrecisionFcn : function(v)
49836     {
49837         return Math.floor(v);
49838     },
49839     
49840     validateValue : function(value)
49841     {
49842         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49843             return false;
49844         }
49845         
49846         var num = this.parseValue(value);
49847         
49848         if(isNaN(num)){
49849             this.markInvalid(String.format(this.nanText, value));
49850             return false;
49851         }
49852         
49853         if(num < this.minValue){
49854             this.markInvalid(String.format(this.minText, this.minValue));
49855             return false;
49856         }
49857         
49858         if(num > this.maxValue){
49859             this.markInvalid(String.format(this.maxText, this.maxValue));
49860             return false;
49861         }
49862         
49863         return true;
49864     },
49865     
49866     validate : function()
49867     {
49868         if(this.disabled || this.allowBlank){
49869             this.markValid();
49870             return true;
49871         }
49872         
49873         var currency = this.getCurrency();
49874         
49875         if(this.validateValue(this.getRawValue()) && currency.length){
49876             this.markValid();
49877             return true;
49878         }
49879         
49880         this.markInvalid();
49881         return false;
49882     },
49883     
49884     getName: function()
49885     {
49886         return this.name;
49887     },
49888     
49889     beforeBlur : function()
49890     {
49891         if(!this.castInt){
49892             return;
49893         }
49894         
49895         var v = this.parseValue(this.getRawValue());
49896         
49897         if(v || v == 0){
49898             this.setValue(v);
49899         }
49900     },
49901     
49902     onBlur : function()
49903     {
49904         this.beforeBlur();
49905         
49906         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49907             //this.el.removeClass(this.focusClass);
49908         }
49909         
49910         this.hasFocus = false;
49911         
49912         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49913             this.validate();
49914         }
49915         
49916         var v = this.getValue();
49917         
49918         if(String(v) !== String(this.startValue)){
49919             this.fireEvent('change', this, v, this.startValue);
49920         }
49921         
49922         this.fireEvent("blur", this);
49923     },
49924     
49925     inputEl : function()
49926     {
49927         return this.el.select('.roo-money-amount-input', true).first();
49928     },
49929     
49930     currencyEl : function()
49931     {
49932         return this.el.select('.roo-money-currency-input', true).first();
49933     },
49934     
49935     hiddenEl : function()
49936     {
49937         return this.el.select('input.hidden-number-input',true).first();
49938     }
49939     
49940 });/**
49941  * @class Roo.bootstrap.BezierSignature
49942  * @extends Roo.bootstrap.Component
49943  * Bootstrap BezierSignature class
49944  * This script refer to:
49945  *    Title: Signature Pad
49946  *    Author: szimek
49947  *    Availability: https://github.com/szimek/signature_pad
49948  *
49949  * @constructor
49950  * Create a new BezierSignature
49951  * @param {Object} config The config object
49952  */
49953
49954 Roo.bootstrap.BezierSignature = function(config){
49955     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49956     this.addEvents({
49957         "resize" : true
49958     });
49959 };
49960
49961 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49962 {
49963      
49964     curve_data: [],
49965     
49966     is_empty: true,
49967     
49968     mouse_btn_down: true,
49969     
49970     /**
49971      * @cfg {int} canvas height
49972      */
49973     canvas_height: '200px',
49974     
49975     /**
49976      * @cfg {float|function} Radius of a single dot.
49977      */ 
49978     dot_size: false,
49979     
49980     /**
49981      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49982      */
49983     min_width: 0.5,
49984     
49985     /**
49986      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49987      */
49988     max_width: 2.5,
49989     
49990     /**
49991      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49992      */
49993     throttle: 16,
49994     
49995     /**
49996      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49997      */
49998     min_distance: 5,
49999     
50000     /**
50001      * @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.
50002      */
50003     bg_color: 'rgba(0, 0, 0, 0)',
50004     
50005     /**
50006      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
50007      */
50008     dot_color: 'black',
50009     
50010     /**
50011      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
50012      */ 
50013     velocity_filter_weight: 0.7,
50014     
50015     /**
50016      * @cfg {function} Callback when stroke begin. 
50017      */
50018     onBegin: false,
50019     
50020     /**
50021      * @cfg {function} Callback when stroke end.
50022      */
50023     onEnd: false,
50024     
50025     getAutoCreate : function()
50026     {
50027         var cls = 'roo-signature column';
50028         
50029         if(this.cls){
50030             cls += ' ' + this.cls;
50031         }
50032         
50033         var col_sizes = [
50034             'lg',
50035             'md',
50036             'sm',
50037             'xs'
50038         ];
50039         
50040         for(var i = 0; i < col_sizes.length; i++) {
50041             if(this[col_sizes[i]]) {
50042                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
50043             }
50044         }
50045         
50046         var cfg = {
50047             tag: 'div',
50048             cls: cls,
50049             cn: [
50050                 {
50051                     tag: 'div',
50052                     cls: 'roo-signature-body',
50053                     cn: [
50054                         {
50055                             tag: 'canvas',
50056                             cls: 'roo-signature-body-canvas',
50057                             height: this.canvas_height,
50058                             width: this.canvas_width
50059                         }
50060                     ]
50061                 },
50062                 {
50063                     tag: 'input',
50064                     type: 'file',
50065                     style: 'display: none'
50066                 }
50067             ]
50068         };
50069         
50070         return cfg;
50071     },
50072     
50073     initEvents: function() 
50074     {
50075         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
50076         
50077         var canvas = this.canvasEl();
50078         
50079         // mouse && touch event swapping...
50080         canvas.dom.style.touchAction = 'none';
50081         canvas.dom.style.msTouchAction = 'none';
50082         
50083         this.mouse_btn_down = false;
50084         canvas.on('mousedown', this._handleMouseDown, this);
50085         canvas.on('mousemove', this._handleMouseMove, this);
50086         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
50087         
50088         if (window.PointerEvent) {
50089             canvas.on('pointerdown', this._handleMouseDown, this);
50090             canvas.on('pointermove', this._handleMouseMove, this);
50091             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
50092         }
50093         
50094         if ('ontouchstart' in window) {
50095             canvas.on('touchstart', this._handleTouchStart, this);
50096             canvas.on('touchmove', this._handleTouchMove, this);
50097             canvas.on('touchend', this._handleTouchEnd, this);
50098         }
50099         
50100         Roo.EventManager.onWindowResize(this.resize, this, true);
50101         
50102         // file input event
50103         this.fileEl().on('change', this.uploadImage, this);
50104         
50105         this.clear();
50106         
50107         this.resize();
50108     },
50109     
50110     resize: function(){
50111         
50112         var canvas = this.canvasEl().dom;
50113         var ctx = this.canvasElCtx();
50114         var img_data = false;
50115         
50116         if(canvas.width > 0) {
50117             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
50118         }
50119         // setting canvas width will clean img data
50120         canvas.width = 0;
50121         
50122         var style = window.getComputedStyle ? 
50123             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
50124             
50125         var padding_left = parseInt(style.paddingLeft) || 0;
50126         var padding_right = parseInt(style.paddingRight) || 0;
50127         
50128         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
50129         
50130         if(img_data) {
50131             ctx.putImageData(img_data, 0, 0);
50132         }
50133     },
50134     
50135     _handleMouseDown: function(e)
50136     {
50137         if (e.browserEvent.which === 1) {
50138             this.mouse_btn_down = true;
50139             this.strokeBegin(e);
50140         }
50141     },
50142     
50143     _handleMouseMove: function (e)
50144     {
50145         if (this.mouse_btn_down) {
50146             this.strokeMoveUpdate(e);
50147         }
50148     },
50149     
50150     _handleMouseUp: function (e)
50151     {
50152         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
50153             this.mouse_btn_down = false;
50154             this.strokeEnd(e);
50155         }
50156     },
50157     
50158     _handleTouchStart: function (e) {
50159         
50160         e.preventDefault();
50161         if (e.browserEvent.targetTouches.length === 1) {
50162             // var touch = e.browserEvent.changedTouches[0];
50163             // this.strokeBegin(touch);
50164             
50165              this.strokeBegin(e); // assume e catching the correct xy...
50166         }
50167     },
50168     
50169     _handleTouchMove: function (e) {
50170         e.preventDefault();
50171         // var touch = event.targetTouches[0];
50172         // _this._strokeMoveUpdate(touch);
50173         this.strokeMoveUpdate(e);
50174     },
50175     
50176     _handleTouchEnd: function (e) {
50177         var wasCanvasTouched = e.target === this.canvasEl().dom;
50178         if (wasCanvasTouched) {
50179             e.preventDefault();
50180             // var touch = event.changedTouches[0];
50181             // _this._strokeEnd(touch);
50182             this.strokeEnd(e);
50183         }
50184     },
50185     
50186     reset: function () {
50187         this._lastPoints = [];
50188         this._lastVelocity = 0;
50189         this._lastWidth = (this.min_width + this.max_width) / 2;
50190         this.canvasElCtx().fillStyle = this.dot_color;
50191     },
50192     
50193     strokeMoveUpdate: function(e)
50194     {
50195         this.strokeUpdate(e);
50196         
50197         if (this.throttle) {
50198             this.throttleStroke(this.strokeUpdate, this.throttle);
50199         }
50200         else {
50201             this.strokeUpdate(e);
50202         }
50203     },
50204     
50205     strokeBegin: function(e)
50206     {
50207         var newPointGroup = {
50208             color: this.dot_color,
50209             points: []
50210         };
50211         
50212         if (typeof this.onBegin === 'function') {
50213             this.onBegin(e);
50214         }
50215         
50216         this.curve_data.push(newPointGroup);
50217         this.reset();
50218         this.strokeUpdate(e);
50219     },
50220     
50221     strokeUpdate: function(e)
50222     {
50223         var rect = this.canvasEl().dom.getBoundingClientRect();
50224         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
50225         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
50226         var lastPoints = lastPointGroup.points;
50227         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
50228         var isLastPointTooClose = lastPoint
50229             ? point.distanceTo(lastPoint) <= this.min_distance
50230             : false;
50231         var color = lastPointGroup.color;
50232         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
50233             var curve = this.addPoint(point);
50234             if (!lastPoint) {
50235                 this.drawDot({color: color, point: point});
50236             }
50237             else if (curve) {
50238                 this.drawCurve({color: color, curve: curve});
50239             }
50240             lastPoints.push({
50241                 time: point.time,
50242                 x: point.x,
50243                 y: point.y
50244             });
50245         }
50246     },
50247     
50248     strokeEnd: function(e)
50249     {
50250         this.strokeUpdate(e);
50251         if (typeof this.onEnd === 'function') {
50252             this.onEnd(e);
50253         }
50254     },
50255     
50256     addPoint:  function (point) {
50257         var _lastPoints = this._lastPoints;
50258         _lastPoints.push(point);
50259         if (_lastPoints.length > 2) {
50260             if (_lastPoints.length === 3) {
50261                 _lastPoints.unshift(_lastPoints[0]);
50262             }
50263             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
50264             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
50265             _lastPoints.shift();
50266             return curve;
50267         }
50268         return null;
50269     },
50270     
50271     calculateCurveWidths: function (startPoint, endPoint) {
50272         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
50273             (1 - this.velocity_filter_weight) * this._lastVelocity;
50274
50275         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
50276         var widths = {
50277             end: newWidth,
50278             start: this._lastWidth
50279         };
50280         
50281         this._lastVelocity = velocity;
50282         this._lastWidth = newWidth;
50283         return widths;
50284     },
50285     
50286     drawDot: function (_a) {
50287         var color = _a.color, point = _a.point;
50288         var ctx = this.canvasElCtx();
50289         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
50290         ctx.beginPath();
50291         this.drawCurveSegment(point.x, point.y, width);
50292         ctx.closePath();
50293         ctx.fillStyle = color;
50294         ctx.fill();
50295     },
50296     
50297     drawCurve: function (_a) {
50298         var color = _a.color, curve = _a.curve;
50299         var ctx = this.canvasElCtx();
50300         var widthDelta = curve.endWidth - curve.startWidth;
50301         var drawSteps = Math.floor(curve.length()) * 2;
50302         ctx.beginPath();
50303         ctx.fillStyle = color;
50304         for (var i = 0; i < drawSteps; i += 1) {
50305         var t = i / drawSteps;
50306         var tt = t * t;
50307         var ttt = tt * t;
50308         var u = 1 - t;
50309         var uu = u * u;
50310         var uuu = uu * u;
50311         var x = uuu * curve.startPoint.x;
50312         x += 3 * uu * t * curve.control1.x;
50313         x += 3 * u * tt * curve.control2.x;
50314         x += ttt * curve.endPoint.x;
50315         var y = uuu * curve.startPoint.y;
50316         y += 3 * uu * t * curve.control1.y;
50317         y += 3 * u * tt * curve.control2.y;
50318         y += ttt * curve.endPoint.y;
50319         var width = curve.startWidth + ttt * widthDelta;
50320         this.drawCurveSegment(x, y, width);
50321         }
50322         ctx.closePath();
50323         ctx.fill();
50324     },
50325     
50326     drawCurveSegment: function (x, y, width) {
50327         var ctx = this.canvasElCtx();
50328         ctx.moveTo(x, y);
50329         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
50330         this.is_empty = false;
50331     },
50332     
50333     clear: function()
50334     {
50335         var ctx = this.canvasElCtx();
50336         var canvas = this.canvasEl().dom;
50337         ctx.fillStyle = this.bg_color;
50338         ctx.clearRect(0, 0, canvas.width, canvas.height);
50339         ctx.fillRect(0, 0, canvas.width, canvas.height);
50340         this.curve_data = [];
50341         this.reset();
50342         this.is_empty = true;
50343     },
50344     
50345     fileEl: function()
50346     {
50347         return  this.el.select('input',true).first();
50348     },
50349     
50350     canvasEl: function()
50351     {
50352         return this.el.select('canvas',true).first();
50353     },
50354     
50355     canvasElCtx: function()
50356     {
50357         return this.el.select('canvas',true).first().dom.getContext('2d');
50358     },
50359     
50360     getImage: function(type)
50361     {
50362         if(this.is_empty) {
50363             return false;
50364         }
50365         
50366         // encryption ?
50367         return this.canvasEl().dom.toDataURL('image/'+type, 1);
50368     },
50369     
50370     drawFromImage: function(img_src)
50371     {
50372         var img = new Image();
50373         
50374         img.onload = function(){
50375             this.canvasElCtx().drawImage(img, 0, 0);
50376         }.bind(this);
50377         
50378         img.src = img_src;
50379         
50380         this.is_empty = false;
50381     },
50382     
50383     selectImage: function()
50384     {
50385         this.fileEl().dom.click();
50386     },
50387     
50388     uploadImage: function(e)
50389     {
50390         var reader = new FileReader();
50391         
50392         reader.onload = function(e){
50393             var img = new Image();
50394             img.onload = function(){
50395                 this.reset();
50396                 this.canvasElCtx().drawImage(img, 0, 0);
50397             }.bind(this);
50398             img.src = e.target.result;
50399         }.bind(this);
50400         
50401         reader.readAsDataURL(e.target.files[0]);
50402     },
50403     
50404     // Bezier Point Constructor
50405     Point: (function () {
50406         function Point(x, y, time) {
50407             this.x = x;
50408             this.y = y;
50409             this.time = time || Date.now();
50410         }
50411         Point.prototype.distanceTo = function (start) {
50412             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50413         };
50414         Point.prototype.equals = function (other) {
50415             return this.x === other.x && this.y === other.y && this.time === other.time;
50416         };
50417         Point.prototype.velocityFrom = function (start) {
50418             return this.time !== start.time
50419             ? this.distanceTo(start) / (this.time - start.time)
50420             : 0;
50421         };
50422         return Point;
50423     }()),
50424     
50425     
50426     // Bezier Constructor
50427     Bezier: (function () {
50428         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50429             this.startPoint = startPoint;
50430             this.control2 = control2;
50431             this.control1 = control1;
50432             this.endPoint = endPoint;
50433             this.startWidth = startWidth;
50434             this.endWidth = endWidth;
50435         }
50436         Bezier.fromPoints = function (points, widths, scope) {
50437             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50438             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50439             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50440         };
50441         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50442             var dx1 = s1.x - s2.x;
50443             var dy1 = s1.y - s2.y;
50444             var dx2 = s2.x - s3.x;
50445             var dy2 = s2.y - s3.y;
50446             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50447             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50448             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50449             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50450             var dxm = m1.x - m2.x;
50451             var dym = m1.y - m2.y;
50452             var k = l2 / (l1 + l2);
50453             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50454             var tx = s2.x - cm.x;
50455             var ty = s2.y - cm.y;
50456             return {
50457                 c1: new scope.Point(m1.x + tx, m1.y + ty),
50458                 c2: new scope.Point(m2.x + tx, m2.y + ty)
50459             };
50460         };
50461         Bezier.prototype.length = function () {
50462             var steps = 10;
50463             var length = 0;
50464             var px;
50465             var py;
50466             for (var i = 0; i <= steps; i += 1) {
50467                 var t = i / steps;
50468                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50469                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50470                 if (i > 0) {
50471                     var xdiff = cx - px;
50472                     var ydiff = cy - py;
50473                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50474                 }
50475                 px = cx;
50476                 py = cy;
50477             }
50478             return length;
50479         };
50480         Bezier.prototype.point = function (t, start, c1, c2, end) {
50481             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50482             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50483             + (3.0 * c2 * (1.0 - t) * t * t)
50484             + (end * t * t * t);
50485         };
50486         return Bezier;
50487     }()),
50488     
50489     throttleStroke: function(fn, wait) {
50490       if (wait === void 0) { wait = 250; }
50491       var previous = 0;
50492       var timeout = null;
50493       var result;
50494       var storedContext;
50495       var storedArgs;
50496       var later = function () {
50497           previous = Date.now();
50498           timeout = null;
50499           result = fn.apply(storedContext, storedArgs);
50500           if (!timeout) {
50501               storedContext = null;
50502               storedArgs = [];
50503           }
50504       };
50505       return function wrapper() {
50506           var args = [];
50507           for (var _i = 0; _i < arguments.length; _i++) {
50508               args[_i] = arguments[_i];
50509           }
50510           var now = Date.now();
50511           var remaining = wait - (now - previous);
50512           storedContext = this;
50513           storedArgs = args;
50514           if (remaining <= 0 || remaining > wait) {
50515               if (timeout) {
50516                   clearTimeout(timeout);
50517                   timeout = null;
50518               }
50519               previous = now;
50520               result = fn.apply(storedContext, storedArgs);
50521               if (!timeout) {
50522                   storedContext = null;
50523                   storedArgs = [];
50524               }
50525           }
50526           else if (!timeout) {
50527               timeout = window.setTimeout(later, remaining);
50528           }
50529           return result;
50530       };
50531   }
50532   
50533 });
50534
50535  
50536
50537  // old names for form elements
50538 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
50539 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
50540 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
50541 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
50542 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
50543 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
50544 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
50545 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
50546 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
50547 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
50548 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
50549 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
50550 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
50551 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
50552 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
50553 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
50554 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
50555 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
50556 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
50557 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
50558 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
50559 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
50560 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
50561 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
50562 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
50563 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
50564
50565 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
50566 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50567
50568 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
50569 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
50570
50571 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
50572 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50573 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
50574 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
50575