roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};
21 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(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: 'about:blank',
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
12205     // All these messages and functions are configurable
12206     return {
12207         /**
12208          * The function used to validate email addresses
12209          * @param {String} value The email address
12210          */
12211         email : function(v){
12212             return email.test(v);
12213         },
12214         /**
12215          * The error text to display when the email validation function returns false
12216          * @type String
12217          */
12218         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12219         /**
12220          * The keystroke filter mask to be applied on email input
12221          * @type RegExp
12222          */
12223         emailMask : /[a-z0-9_\.\-@]/i,
12224
12225         /**
12226          * The function used to validate URLs
12227          * @param {String} value The URL
12228          */
12229         url : function(v){
12230             return url.test(v);
12231         },
12232         /**
12233          * The error text to display when the url validation function returns false
12234          * @type String
12235          */
12236         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12237         
12238         /**
12239          * The function used to validate alpha values
12240          * @param {String} value The value
12241          */
12242         alpha : function(v){
12243             return alpha.test(v);
12244         },
12245         /**
12246          * The error text to display when the alpha validation function returns false
12247          * @type String
12248          */
12249         alphaText : 'This field should only contain letters and _',
12250         /**
12251          * The keystroke filter mask to be applied on alpha input
12252          * @type RegExp
12253          */
12254         alphaMask : /[a-z_]/i,
12255
12256         /**
12257          * The function used to validate alphanumeric values
12258          * @param {String} value The value
12259          */
12260         alphanum : function(v){
12261             return alphanum.test(v);
12262         },
12263         /**
12264          * The error text to display when the alphanumeric validation function returns false
12265          * @type String
12266          */
12267         alphanumText : 'This field should only contain letters, numbers and _',
12268         /**
12269          * The keystroke filter mask to be applied on alphanumeric input
12270          * @type RegExp
12271          */
12272         alphanumMask : /[a-z0-9_]/i
12273     };
12274 }();/*
12275  * - LGPL
12276  *
12277  * Input
12278  * 
12279  */
12280
12281 /**
12282  * @class Roo.bootstrap.form.Input
12283  * @extends Roo.bootstrap.Component
12284  * Bootstrap Input class
12285  * @cfg {Boolean} disabled is it disabled
12286  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12287  * @cfg {String} name name of the input
12288  * @cfg {string} fieldLabel - the label associated
12289  * @cfg {string} placeholder - placeholder to put in text.
12290  * @cfg {string} before - input group add on before
12291  * @cfg {string} after - input group add on after
12292  * @cfg {string} size - (lg|sm) or leave empty..
12293  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12294  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12295  * @cfg {Number} md colspan out of 12 for computer-sized screens
12296  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12297  * @cfg {string} value default value of the input
12298  * @cfg {Number} labelWidth set the width of label 
12299  * @cfg {Number} labellg set the width of label (1-12)
12300  * @cfg {Number} labelmd set the width of label (1-12)
12301  * @cfg {Number} labelsm set the width of label (1-12)
12302  * @cfg {Number} labelxs set the width of label (1-12)
12303  * @cfg {String} labelAlign (top|left)
12304  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12305  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12306  * @cfg {String} indicatorpos (left|right) default left
12307  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12308  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12309  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12310  * @cfg {Roo.bootstrap.Button} before Button to show before
12311  * @cfg {Roo.bootstrap.Button} afterButton to show before
12312  * @cfg {String} align (left|center|right) Default left
12313  * @cfg {Boolean} forceFeedback (true|false) Default false
12314  * 
12315  * @constructor
12316  * Create a new Input
12317  * @param {Object} config The config object
12318  */
12319
12320 Roo.bootstrap.form.Input = function(config){
12321     
12322     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12323     
12324     this.addEvents({
12325         /**
12326          * @event focus
12327          * Fires when this field receives input focus.
12328          * @param {Roo.form.Field} this
12329          */
12330         focus : true,
12331         /**
12332          * @event blur
12333          * Fires when this field loses input focus.
12334          * @param {Roo.form.Field} this
12335          */
12336         blur : true,
12337         /**
12338          * @event specialkey
12339          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12340          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12341          * @param {Roo.form.Field} this
12342          * @param {Roo.EventObject} e The event object
12343          */
12344         specialkey : true,
12345         /**
12346          * @event change
12347          * Fires just before the field blurs if the field value has changed.
12348          * @param {Roo.form.Field} this
12349          * @param {Mixed} newValue The new value
12350          * @param {Mixed} oldValue The original value
12351          */
12352         change : true,
12353         /**
12354          * @event invalid
12355          * Fires after the field has been marked as invalid.
12356          * @param {Roo.form.Field} this
12357          * @param {String} msg The validation message
12358          */
12359         invalid : true,
12360         /**
12361          * @event valid
12362          * Fires after the field has been validated with no errors.
12363          * @param {Roo.form.Field} this
12364          */
12365         valid : true,
12366          /**
12367          * @event keyup
12368          * Fires after the key up
12369          * @param {Roo.form.Field} this
12370          * @param {Roo.EventObject}  e The event Object
12371          */
12372         keyup : true,
12373         /**
12374          * @event paste
12375          * Fires after the user pastes into input
12376          * @param {Roo.form.Field} this
12377          * @param {Roo.EventObject}  e The event Object
12378          */
12379         paste : true
12380     });
12381 };
12382
12383 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12384      /**
12385      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12386       automatic validation (defaults to "keyup").
12387      */
12388     validationEvent : "keyup",
12389      /**
12390      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12391      */
12392     validateOnBlur : true,
12393     /**
12394      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12395      */
12396     validationDelay : 250,
12397      /**
12398      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12399      */
12400     focusClass : "x-form-focus",  // not needed???
12401     
12402        
12403     /**
12404      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12405      */
12406     invalidClass : "has-warning",
12407     
12408     /**
12409      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12410      */
12411     validClass : "has-success",
12412     
12413     /**
12414      * @cfg {Boolean} hasFeedback (true|false) default true
12415      */
12416     hasFeedback : true,
12417     
12418     /**
12419      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12420      */
12421     invalidFeedbackClass : "glyphicon-warning-sign",
12422     
12423     /**
12424      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12425      */
12426     validFeedbackClass : "glyphicon-ok",
12427     
12428     /**
12429      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12430      */
12431     selectOnFocus : false,
12432     
12433      /**
12434      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12435      */
12436     maskRe : null,
12437        /**
12438      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12439      */
12440     vtype : null,
12441     
12442       /**
12443      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12444      */
12445     disableKeyFilter : false,
12446     
12447        /**
12448      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12449      */
12450     disabled : false,
12451      /**
12452      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12453      */
12454     allowBlank : true,
12455     /**
12456      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12457      */
12458     blankText : "Please complete this mandatory field",
12459     
12460      /**
12461      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12462      */
12463     minLength : 0,
12464     /**
12465      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12466      */
12467     maxLength : Number.MAX_VALUE,
12468     /**
12469      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12470      */
12471     minLengthText : "The minimum length for this field is {0}",
12472     /**
12473      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12474      */
12475     maxLengthText : "The maximum length for this field is {0}",
12476   
12477     
12478     /**
12479      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12480      * If available, this function will be called only after the basic validators all return true, and will be passed the
12481      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12482      */
12483     validator : null,
12484     /**
12485      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12486      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12487      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12488      */
12489     regex : null,
12490     /**
12491      * @cfg {String} regexText -- Depricated - use Invalid Text
12492      */
12493     regexText : "",
12494     
12495     /**
12496      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12497      */
12498     invalidText : "",
12499     
12500     
12501     
12502     autocomplete: false,
12503     
12504     
12505     fieldLabel : '',
12506     inputType : 'text',
12507     
12508     name : false,
12509     placeholder: false,
12510     before : false,
12511     after : false,
12512     size : false,
12513     hasFocus : false,
12514     preventMark: false,
12515     isFormField : true,
12516     value : '',
12517     labelWidth : 2,
12518     labelAlign : false,
12519     readOnly : false,
12520     align : false,
12521     formatedValue : false,
12522     forceFeedback : false,
12523     
12524     indicatorpos : 'left',
12525     
12526     labellg : 0,
12527     labelmd : 0,
12528     labelsm : 0,
12529     labelxs : 0,
12530     
12531     capture : '',
12532     accept : '',
12533     
12534     parentLabelAlign : function()
12535     {
12536         var parent = this;
12537         while (parent.parent()) {
12538             parent = parent.parent();
12539             if (typeof(parent.labelAlign) !='undefined') {
12540                 return parent.labelAlign;
12541             }
12542         }
12543         return 'left';
12544         
12545     },
12546     
12547     getAutoCreate : function()
12548     {
12549         
12550         var id = Roo.id();
12551         
12552         var cfg = {};
12553         
12554         if(this.inputType != 'hidden'){
12555             cfg.cls = 'form-group' //input-group
12556         }
12557         
12558         var input =  {
12559             tag: 'input',
12560             id : id,
12561             type : this.inputType,
12562             value : this.value,
12563             cls : 'form-control',
12564             placeholder : this.placeholder || '',
12565             autocomplete : this.autocomplete || 'new-password'
12566         };
12567         if (this.inputType == 'file') {
12568             input.style = 'overflow:hidden'; // why not in CSS?
12569         }
12570         
12571         if(this.capture.length){
12572             input.capture = this.capture;
12573         }
12574         
12575         if(this.accept.length){
12576             input.accept = this.accept + "/*";
12577         }
12578         
12579         if(this.align){
12580             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12581         }
12582         
12583         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12584             input.maxLength = this.maxLength;
12585         }
12586         
12587         if (this.disabled) {
12588             input.disabled=true;
12589         }
12590         
12591         if (this.readOnly) {
12592             input.readonly=true;
12593         }
12594         
12595         if (this.name) {
12596             input.name = this.name;
12597         }
12598         
12599         if (this.size) {
12600             input.cls += ' input-' + this.size;
12601         }
12602         
12603         var settings=this;
12604         ['xs','sm','md','lg'].map(function(size){
12605             if (settings[size]) {
12606                 cfg.cls += ' col-' + size + '-' + settings[size];
12607             }
12608         });
12609         
12610         var inputblock = input;
12611         
12612         var feedback = {
12613             tag: 'span',
12614             cls: 'glyphicon form-control-feedback'
12615         };
12616             
12617         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12618             
12619             inputblock = {
12620                 cls : 'has-feedback',
12621                 cn :  [
12622                     input,
12623                     feedback
12624                 ] 
12625             };  
12626         }
12627         
12628         if (this.before || this.after) {
12629             
12630             inputblock = {
12631                 cls : 'input-group',
12632                 cn :  [] 
12633             };
12634             
12635             if (this.before && typeof(this.before) == 'string') {
12636                 
12637                 inputblock.cn.push({
12638                     tag :'span',
12639                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12640                     html : this.before
12641                 });
12642             }
12643             if (this.before && typeof(this.before) == 'object') {
12644                 this.before = Roo.factory(this.before);
12645                 
12646                 inputblock.cn.push({
12647                     tag :'span',
12648                     cls : 'roo-input-before input-group-prepend   input-group-' +
12649                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12650                 });
12651             }
12652             
12653             inputblock.cn.push(input);
12654             
12655             if (this.after && typeof(this.after) == 'string') {
12656                 inputblock.cn.push({
12657                     tag :'span',
12658                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12659                     html : this.after
12660                 });
12661             }
12662             if (this.after && typeof(this.after) == 'object') {
12663                 this.after = Roo.factory(this.after);
12664                 
12665                 inputblock.cn.push({
12666                     tag :'span',
12667                     cls : 'roo-input-after input-group-append  input-group-' +
12668                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12669                 });
12670             }
12671             
12672             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12673                 inputblock.cls += ' has-feedback';
12674                 inputblock.cn.push(feedback);
12675             }
12676         };
12677         
12678         
12679         
12680         cfg = this.getAutoCreateLabel( cfg, inputblock );
12681         
12682        
12683          
12684         
12685         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12686            cfg.cls += ' navbar-form';
12687         }
12688         
12689         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12690             // on BS4 we do this only if not form 
12691             cfg.cls += ' navbar-form';
12692             cfg.tag = 'li';
12693         }
12694         
12695         return cfg;
12696         
12697     },
12698     /**
12699      * autocreate the label - also used by textara... ?? and others?
12700      */
12701     getAutoCreateLabel : function( cfg, inputblock )
12702     {
12703         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12704        
12705         var indicator = {
12706             tag : 'i',
12707             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12708             tooltip : 'This field is required'
12709         };
12710         if (this.allowBlank ) {
12711             indicator.style = this.allowBlank ? ' display:none' : '';
12712         }
12713         if (align ==='left' && this.fieldLabel.length) {
12714             
12715             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12716             
12717             cfg.cn = [
12718                 indicator,
12719                 {
12720                     tag: 'label',
12721                     'for' :  id,
12722                     cls : 'control-label col-form-label',
12723                     html : this.fieldLabel
12724
12725                 },
12726                 {
12727                     cls : "", 
12728                     cn: [
12729                         inputblock
12730                     ]
12731                 }
12732             ];
12733             
12734             var labelCfg = cfg.cn[1];
12735             var contentCfg = cfg.cn[2];
12736             
12737             if(this.indicatorpos == 'right'){
12738                 cfg.cn = [
12739                     {
12740                         tag: 'label',
12741                         'for' :  id,
12742                         cls : 'control-label col-form-label',
12743                         cn : [
12744                             {
12745                                 tag : 'span',
12746                                 html : this.fieldLabel
12747                             },
12748                             indicator
12749                         ]
12750                     },
12751                     {
12752                         cls : "",
12753                         cn: [
12754                             inputblock
12755                         ]
12756                     }
12757
12758                 ];
12759                 
12760                 labelCfg = cfg.cn[0];
12761                 contentCfg = cfg.cn[1];
12762             
12763             }
12764             
12765             if(this.labelWidth > 12){
12766                 labelCfg.style = "width: " + this.labelWidth + 'px';
12767             }
12768             
12769             if(this.labelWidth < 13 && this.labelmd == 0){
12770                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12771             }
12772             
12773             if(this.labellg > 0){
12774                 labelCfg.cls += ' col-lg-' + this.labellg;
12775                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12776             }
12777             
12778             if(this.labelmd > 0){
12779                 labelCfg.cls += ' col-md-' + this.labelmd;
12780                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12781             }
12782             
12783             if(this.labelsm > 0){
12784                 labelCfg.cls += ' col-sm-' + this.labelsm;
12785                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12786             }
12787             
12788             if(this.labelxs > 0){
12789                 labelCfg.cls += ' col-xs-' + this.labelxs;
12790                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12791             }
12792             
12793             
12794         } else if ( this.fieldLabel.length) {
12795                 
12796             
12797             
12798             cfg.cn = [
12799                 {
12800                     tag : 'i',
12801                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12802                     tooltip : 'This field is required',
12803                     style : this.allowBlank ? ' display:none' : '' 
12804                 },
12805                 {
12806                     tag: 'label',
12807                    //cls : 'input-group-addon',
12808                     html : this.fieldLabel
12809
12810                 },
12811
12812                inputblock
12813
12814            ];
12815            
12816            if(this.indicatorpos == 'right'){
12817        
12818                 cfg.cn = [
12819                     {
12820                         tag: 'label',
12821                        //cls : 'input-group-addon',
12822                         html : this.fieldLabel
12823
12824                     },
12825                     {
12826                         tag : 'i',
12827                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12828                         tooltip : 'This field is required',
12829                         style : this.allowBlank ? ' display:none' : '' 
12830                     },
12831
12832                    inputblock
12833
12834                ];
12835
12836             }
12837
12838         } else {
12839             
12840             cfg.cn = [
12841
12842                     inputblock
12843
12844             ];
12845                 
12846                 
12847         };
12848         return cfg;
12849     },
12850     
12851     
12852     /**
12853      * return the real input element.
12854      */
12855     inputEl: function ()
12856     {
12857         return this.el.select('input.form-control',true).first();
12858     },
12859     
12860     tooltipEl : function()
12861     {
12862         return this.inputEl();
12863     },
12864     
12865     indicatorEl : function()
12866     {
12867         if (Roo.bootstrap.version == 4) {
12868             return false; // not enabled in v4 yet.
12869         }
12870         
12871         var indicator = this.el.select('i.roo-required-indicator',true).first();
12872         
12873         if(!indicator){
12874             return false;
12875         }
12876         
12877         return indicator;
12878         
12879     },
12880     
12881     setDisabled : function(v)
12882     {
12883         var i  = this.inputEl().dom;
12884         if (!v) {
12885             i.removeAttribute('disabled');
12886             return;
12887             
12888         }
12889         i.setAttribute('disabled','true');
12890     },
12891     initEvents : function()
12892     {
12893           
12894         this.inputEl().on("keydown" , this.fireKey,  this);
12895         this.inputEl().on("focus", this.onFocus,  this);
12896         this.inputEl().on("blur", this.onBlur,  this);
12897         
12898         this.inputEl().relayEvent('keyup', this);
12899         this.inputEl().relayEvent('paste', this);
12900         
12901         this.indicator = this.indicatorEl();
12902         
12903         if(this.indicator){
12904             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12905         }
12906  
12907         // reference to original value for reset
12908         this.originalValue = this.getValue();
12909         //Roo.form.TextField.superclass.initEvents.call(this);
12910         if(this.validationEvent == 'keyup'){
12911             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12912             this.inputEl().on('keyup', this.filterValidation, this);
12913         }
12914         else if(this.validationEvent !== false){
12915             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12916         }
12917         
12918         if(this.selectOnFocus){
12919             this.on("focus", this.preFocus, this);
12920             
12921         }
12922         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12923             this.inputEl().on("keypress", this.filterKeys, this);
12924         } else {
12925             this.inputEl().relayEvent('keypress', this);
12926         }
12927        /* if(this.grow){
12928             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12929             this.el.on("click", this.autoSize,  this);
12930         }
12931         */
12932         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12933             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12934         }
12935         
12936         if (typeof(this.before) == 'object') {
12937             this.before.render(this.el.select('.roo-input-before',true).first());
12938         }
12939         if (typeof(this.after) == 'object') {
12940             this.after.render(this.el.select('.roo-input-after',true).first());
12941         }
12942         
12943         this.inputEl().on('change', this.onChange, this);
12944         
12945     },
12946     filterValidation : function(e){
12947         if(!e.isNavKeyPress()){
12948             this.validationTask.delay(this.validationDelay);
12949         }
12950     },
12951      /**
12952      * Validates the field value
12953      * @return {Boolean} True if the value is valid, else false
12954      */
12955     validate : function(){
12956         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12957         if(this.disabled || this.validateValue(this.getRawValue())){
12958             Roo.log('MARK INVALID');
12959             this.markValid();
12960             return true;
12961         }
12962         
12963         this.markInvalid();
12964         return false;
12965     },
12966     
12967     
12968     /**
12969      * Validates a value according to the field's validation rules and marks the field as invalid
12970      * if the validation fails
12971      * @param {Mixed} value The value to validate
12972      * @return {Boolean} True if the value is valid, else false
12973      */
12974     validateValue : function(value)
12975     {
12976         if(this.getVisibilityEl().hasClass('hidden')){
12977             return true;
12978         }
12979         
12980         if(value.length < 1)  { // if it's blank
12981             if(this.allowBlank){
12982                 return true;
12983             }
12984             return false;
12985         }
12986         
12987         if(value.length < this.minLength){
12988             return false;
12989         }
12990         if(value.length > this.maxLength){
12991             return false;
12992         }
12993         if(this.vtype){
12994             var vt = Roo.form.VTypes;
12995             if(!vt[this.vtype](value, this)){
12996                 return false;
12997             }
12998         }
12999         if(typeof this.validator == "function"){
13000             var msg = this.validator(value);
13001             if (typeof(msg) == 'string') {
13002                 this.invalidText = msg;
13003             }
13004             if(msg !== true){
13005                 return false;
13006             }
13007         }
13008         
13009         if(this.regex && !this.regex.test(value)){
13010             return false;
13011         }
13012         
13013         return true;
13014     },
13015     
13016      // private
13017     fireKey : function(e){
13018         //Roo.log('field ' + e.getKey());
13019         if(e.isNavKeyPress()){
13020             this.fireEvent("specialkey", this, e);
13021         }
13022     },
13023     focus : function (selectText){
13024         if(this.rendered){
13025             this.inputEl().focus();
13026             if(selectText === true){
13027                 this.inputEl().dom.select();
13028             }
13029         }
13030         return this;
13031     } ,
13032     
13033     onFocus : function(){
13034         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13035            // this.el.addClass(this.focusClass);
13036         }
13037         if(!this.hasFocus){
13038             this.hasFocus = true;
13039             this.startValue = this.getValue();
13040             this.fireEvent("focus", this);
13041         }
13042     },
13043     
13044     beforeBlur : Roo.emptyFn,
13045
13046     
13047     // private
13048     onBlur : function(){
13049         this.beforeBlur();
13050         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13051             //this.el.removeClass(this.focusClass);
13052         }
13053         this.hasFocus = false;
13054         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13055             this.validate();
13056         }
13057         var v = this.getValue();
13058         if(String(v) !== String(this.startValue)){
13059             this.fireEvent('change', this, v, this.startValue);
13060         }
13061         this.fireEvent("blur", this);
13062     },
13063     
13064     onChange : function(e)
13065     {
13066         var v = this.getValue();
13067         if(String(v) !== String(this.startValue)){
13068             this.fireEvent('change', this, v, this.startValue);
13069         }
13070         
13071     },
13072     
13073     /**
13074      * Resets the current field value to the originally loaded value and clears any validation messages
13075      */
13076     reset : function(){
13077         this.setValue(this.originalValue);
13078         this.validate();
13079     },
13080      /**
13081      * Returns the name of the field
13082      * @return {Mixed} name The name field
13083      */
13084     getName: function(){
13085         return this.name;
13086     },
13087      /**
13088      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13089      * @return {Mixed} value The field value
13090      */
13091     getValue : function(){
13092         
13093         var v = this.inputEl().getValue();
13094         
13095         return v;
13096     },
13097     /**
13098      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13099      * @return {Mixed} value The field value
13100      */
13101     getRawValue : function(){
13102         var v = this.inputEl().getValue();
13103         
13104         return v;
13105     },
13106     
13107     /**
13108      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13109      * @param {Mixed} value The value to set
13110      */
13111     setRawValue : function(v){
13112         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13113     },
13114     
13115     selectText : function(start, end){
13116         var v = this.getRawValue();
13117         if(v.length > 0){
13118             start = start === undefined ? 0 : start;
13119             end = end === undefined ? v.length : end;
13120             var d = this.inputEl().dom;
13121             if(d.setSelectionRange){
13122                 d.setSelectionRange(start, end);
13123             }else if(d.createTextRange){
13124                 var range = d.createTextRange();
13125                 range.moveStart("character", start);
13126                 range.moveEnd("character", v.length-end);
13127                 range.select();
13128             }
13129         }
13130     },
13131     
13132     /**
13133      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13134      * @param {Mixed} value The value to set
13135      */
13136     setValue : function(v){
13137         this.value = v;
13138         if(this.rendered){
13139             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13140             this.validate();
13141         }
13142     },
13143     
13144     /*
13145     processValue : function(value){
13146         if(this.stripCharsRe){
13147             var newValue = value.replace(this.stripCharsRe, '');
13148             if(newValue !== value){
13149                 this.setRawValue(newValue);
13150                 return newValue;
13151             }
13152         }
13153         return value;
13154     },
13155   */
13156     preFocus : function(){
13157         
13158         if(this.selectOnFocus){
13159             this.inputEl().dom.select();
13160         }
13161     },
13162     filterKeys : function(e){
13163         var k = e.getKey();
13164         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13165             return;
13166         }
13167         var c = e.getCharCode(), cc = String.fromCharCode(c);
13168         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13169             return;
13170         }
13171         if(!this.maskRe.test(cc)){
13172             e.stopEvent();
13173         }
13174     },
13175      /**
13176      * Clear any invalid styles/messages for this field
13177      */
13178     clearInvalid : function(){
13179         
13180         if(!this.el || this.preventMark){ // not rendered
13181             return;
13182         }
13183         
13184         
13185         this.el.removeClass([this.invalidClass, 'is-invalid']);
13186         
13187         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13188             
13189             var feedback = this.el.select('.form-control-feedback', true).first();
13190             
13191             if(feedback){
13192                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13193             }
13194             
13195         }
13196         
13197         if(this.indicator){
13198             this.indicator.removeClass('visible');
13199             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13200         }
13201         
13202         this.fireEvent('valid', this);
13203     },
13204     
13205      /**
13206      * Mark this field as valid
13207      */
13208     markValid : function()
13209     {
13210         if(!this.el  || this.preventMark){ // not rendered...
13211             return;
13212         }
13213         
13214         this.el.removeClass([this.invalidClass, this.validClass]);
13215         this.inputEl().removeClass(['is-valid', 'is-invalid']);
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, this.validFeedbackClass]);
13221         }
13222         
13223         if(this.indicator){
13224             this.indicator.removeClass('visible');
13225             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13226         }
13227         
13228         if(this.disabled){
13229             return;
13230         }
13231         
13232            
13233         if(this.allowBlank && !this.getRawValue().length){
13234             return;
13235         }
13236         if (Roo.bootstrap.version == 3) {
13237             this.el.addClass(this.validClass);
13238         } else {
13239             this.inputEl().addClass('is-valid');
13240         }
13241
13242         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13243             
13244             var feedback = this.el.select('.form-control-feedback', true).first();
13245             
13246             if(feedback){
13247                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13248                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13249             }
13250             
13251         }
13252         
13253         this.fireEvent('valid', this);
13254     },
13255     
13256      /**
13257      * Mark this field as invalid
13258      * @param {String} msg The validation message
13259      */
13260     markInvalid : function(msg)
13261     {
13262         if(!this.el  || this.preventMark){ // not rendered
13263             return;
13264         }
13265         
13266         this.el.removeClass([this.invalidClass, this.validClass]);
13267         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13268         
13269         var feedback = this.el.select('.form-control-feedback', true).first();
13270             
13271         if(feedback){
13272             this.el.select('.form-control-feedback', true).first().removeClass(
13273                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13274         }
13275
13276         if(this.disabled){
13277             return;
13278         }
13279         
13280         if(this.allowBlank && !this.getRawValue().length){
13281             return;
13282         }
13283         
13284         if(this.indicator){
13285             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13286             this.indicator.addClass('visible');
13287         }
13288         if (Roo.bootstrap.version == 3) {
13289             this.el.addClass(this.invalidClass);
13290         } else {
13291             this.inputEl().addClass('is-invalid');
13292         }
13293         
13294         
13295         
13296         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13297             
13298             var feedback = this.el.select('.form-control-feedback', true).first();
13299             
13300             if(feedback){
13301                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13302                 
13303                 if(this.getValue().length || this.forceFeedback){
13304                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13305                 }
13306                 
13307             }
13308             
13309         }
13310         
13311         this.fireEvent('invalid', this, msg);
13312     },
13313     // private
13314     SafariOnKeyDown : function(event)
13315     {
13316         // this is a workaround for a password hang bug on chrome/ webkit.
13317         if (this.inputEl().dom.type != 'password') {
13318             return;
13319         }
13320         
13321         var isSelectAll = false;
13322         
13323         if(this.inputEl().dom.selectionEnd > 0){
13324             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13325         }
13326         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13327             event.preventDefault();
13328             this.setValue('');
13329             return;
13330         }
13331         
13332         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13333             
13334             event.preventDefault();
13335             // this is very hacky as keydown always get's upper case.
13336             //
13337             var cc = String.fromCharCode(event.getCharCode());
13338             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13339             
13340         }
13341     },
13342     adjustWidth : function(tag, w){
13343         tag = tag.toLowerCase();
13344         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13345             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13346                 if(tag == 'input'){
13347                     return w + 2;
13348                 }
13349                 if(tag == 'textarea'){
13350                     return w-2;
13351                 }
13352             }else if(Roo.isOpera){
13353                 if(tag == 'input'){
13354                     return w + 2;
13355                 }
13356                 if(tag == 'textarea'){
13357                     return w-2;
13358                 }
13359             }
13360         }
13361         return w;
13362     },
13363     
13364     setFieldLabel : function(v)
13365     {
13366         if(!this.rendered){
13367             return;
13368         }
13369         
13370         if(this.indicatorEl()){
13371             var ar = this.el.select('label > span',true);
13372             
13373             if (ar.elements.length) {
13374                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13375                 this.fieldLabel = v;
13376                 return;
13377             }
13378             
13379             var br = this.el.select('label',true);
13380             
13381             if(br.elements.length) {
13382                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13383                 this.fieldLabel = v;
13384                 return;
13385             }
13386             
13387             Roo.log('Cannot Found any of label > span || label in input');
13388             return;
13389         }
13390         
13391         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13392         this.fieldLabel = v;
13393         
13394         
13395     }
13396 });
13397
13398  
13399 /*
13400  * - LGPL
13401  *
13402  * Input
13403  * 
13404  */
13405
13406 /**
13407  * @class Roo.bootstrap.form.TextArea
13408  * @extends Roo.bootstrap.form.Input
13409  * Bootstrap TextArea class
13410  * @cfg {Number} cols Specifies the visible width of a text area
13411  * @cfg {Number} rows Specifies the visible number of lines in a text area
13412  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13413  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13414  * @cfg {string} html text
13415  * 
13416  * @constructor
13417  * Create a new TextArea
13418  * @param {Object} config The config object
13419  */
13420
13421 Roo.bootstrap.form.TextArea = function(config){
13422     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13423    
13424 };
13425
13426 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13427      
13428     cols : false,
13429     rows : 5,
13430     readOnly : false,
13431     warp : 'soft',
13432     resize : false,
13433     value: false,
13434     html: false,
13435     
13436     getAutoCreate : function(){
13437         
13438         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13439         
13440         var id = Roo.id();
13441         
13442         var cfg = {};
13443         
13444         if(this.inputType != 'hidden'){
13445             cfg.cls = 'form-group' //input-group
13446         }
13447         
13448         var input =  {
13449             tag: 'textarea',
13450             id : id,
13451             warp : this.warp,
13452             rows : this.rows,
13453             value : this.value || '',
13454             html: this.html || '',
13455             cls : 'form-control',
13456             placeholder : this.placeholder || '' 
13457             
13458         };
13459         
13460         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13461             input.maxLength = this.maxLength;
13462         }
13463         
13464         if(this.resize){
13465             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13466         }
13467         
13468         if(this.cols){
13469             input.cols = this.cols;
13470         }
13471         
13472         if (this.readOnly) {
13473             input.readonly = true;
13474         }
13475         
13476         if (this.name) {
13477             input.name = this.name;
13478         }
13479         
13480         if (this.size) {
13481             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13482         }
13483         
13484         var settings=this;
13485         ['xs','sm','md','lg'].map(function(size){
13486             if (settings[size]) {
13487                 cfg.cls += ' col-' + size + '-' + settings[size];
13488             }
13489         });
13490         
13491         var inputblock = input;
13492         
13493         if(this.hasFeedback && !this.allowBlank){
13494             
13495             var feedback = {
13496                 tag: 'span',
13497                 cls: 'glyphicon form-control-feedback'
13498             };
13499
13500             inputblock = {
13501                 cls : 'has-feedback',
13502                 cn :  [
13503                     input,
13504                     feedback
13505                 ] 
13506             };  
13507         }
13508         
13509         
13510         if (this.before || this.after) {
13511             
13512             inputblock = {
13513                 cls : 'input-group',
13514                 cn :  [] 
13515             };
13516             if (this.before) {
13517                 inputblock.cn.push({
13518                     tag :'span',
13519                     cls : 'input-group-addon',
13520                     html : this.before
13521                 });
13522             }
13523             
13524             inputblock.cn.push(input);
13525             
13526             if(this.hasFeedback && !this.allowBlank){
13527                 inputblock.cls += ' has-feedback';
13528                 inputblock.cn.push(feedback);
13529             }
13530             
13531             if (this.after) {
13532                 inputblock.cn.push({
13533                     tag :'span',
13534                     cls : 'input-group-addon',
13535                     html : this.after
13536                 });
13537             }
13538             
13539         }
13540         
13541         
13542         cfg = this.getAutoCreateLabel( cfg, inputblock );
13543
13544          
13545         
13546         if (this.disabled) {
13547             input.disabled=true;
13548         }
13549         
13550         return cfg;
13551         
13552     },
13553     /**
13554      * return the real textarea element.
13555      */
13556     inputEl: function ()
13557     {
13558         return this.el.select('textarea.form-control',true).first();
13559     },
13560     
13561     /**
13562      * Clear any invalid styles/messages for this field
13563      */
13564     clearInvalid : function()
13565     {
13566         
13567         if(!this.el || this.preventMark){ // not rendered
13568             return;
13569         }
13570         
13571         var label = this.el.select('label', true).first();
13572         //var icon = this.el.select('i.fa-star', true).first();
13573         
13574         //if(label && icon){
13575         //    icon.remove();
13576         //}
13577         this.el.removeClass( this.validClass);
13578         this.inputEl().removeClass('is-invalid');
13579          
13580         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13581             
13582             var feedback = this.el.select('.form-control-feedback', true).first();
13583             
13584             if(feedback){
13585                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13586             }
13587             
13588         }
13589         
13590         this.fireEvent('valid', this);
13591     },
13592     
13593      /**
13594      * Mark this field as valid
13595      */
13596     markValid : function()
13597     {
13598         if(!this.el  || this.preventMark){ // not rendered
13599             return;
13600         }
13601         
13602         this.el.removeClass([this.invalidClass, this.validClass]);
13603         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13604         
13605         var feedback = this.el.select('.form-control-feedback', true).first();
13606             
13607         if(feedback){
13608             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13609         }
13610
13611         if(this.disabled || this.allowBlank){
13612             return;
13613         }
13614         
13615         var label = this.el.select('label', true).first();
13616         var icon = this.el.select('i.fa-star', true).first();
13617         
13618         //if(label && icon){
13619         //    icon.remove();
13620         //}
13621         if (Roo.bootstrap.version == 3) {
13622             this.el.addClass(this.validClass);
13623         } else {
13624             this.inputEl().addClass('is-valid');
13625         }
13626         
13627         
13628         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13629             
13630             var feedback = this.el.select('.form-control-feedback', true).first();
13631             
13632             if(feedback){
13633                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13634                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13635             }
13636             
13637         }
13638         
13639         this.fireEvent('valid', this);
13640     },
13641     
13642      /**
13643      * Mark this field as invalid
13644      * @param {String} msg The validation message
13645      */
13646     markInvalid : function(msg)
13647     {
13648         if(!this.el  || this.preventMark){ // not rendered
13649             return;
13650         }
13651         
13652         this.el.removeClass([this.invalidClass, this.validClass]);
13653         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13654         
13655         var feedback = this.el.select('.form-control-feedback', true).first();
13656             
13657         if(feedback){
13658             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13659         }
13660
13661         if(this.disabled || this.allowBlank){
13662             return;
13663         }
13664         
13665         var label = this.el.select('label', true).first();
13666         //var icon = this.el.select('i.fa-star', true).first();
13667         
13668         //if(!this.getValue().length && label && !icon){
13669           /*  this.el.createChild({
13670                 tag : 'i',
13671                 cls : 'text-danger fa fa-lg fa-star',
13672                 tooltip : 'This field is required',
13673                 style : 'margin-right:5px;'
13674             }, label, true);
13675             */
13676         //}
13677         
13678         if (Roo.bootstrap.version == 3) {
13679             this.el.addClass(this.invalidClass);
13680         } else {
13681             this.inputEl().addClass('is-invalid');
13682         }
13683         
13684         // fixme ... this may be depricated need to test..
13685         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13686             
13687             var feedback = this.el.select('.form-control-feedback', true).first();
13688             
13689             if(feedback){
13690                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13691                 
13692                 if(this.getValue().length || this.forceFeedback){
13693                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13694                 }
13695                 
13696             }
13697             
13698         }
13699         
13700         this.fireEvent('invalid', this, msg);
13701     }
13702 });
13703
13704  
13705 /*
13706  * - LGPL
13707  *
13708  * trigger field - base class for combo..
13709  * 
13710  */
13711  
13712 /**
13713  * @class Roo.bootstrap.form.TriggerField
13714  * @extends Roo.bootstrap.form.Input
13715  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13716  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13717  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13718  * for which you can provide a custom implementation.  For example:
13719  * <pre><code>
13720 var trigger = new Roo.bootstrap.form.TriggerField();
13721 trigger.onTriggerClick = myTriggerFn;
13722 trigger.applyTo('my-field');
13723 </code></pre>
13724  *
13725  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13726  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13727  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13728  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13729  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13730
13731  * @constructor
13732  * Create a new TriggerField.
13733  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13734  * to the base TextField)
13735  */
13736 Roo.bootstrap.form.TriggerField = function(config){
13737     this.mimicing = false;
13738     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13739 };
13740
13741 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13742     /**
13743      * @cfg {String} triggerClass A CSS class to apply to the trigger
13744      */
13745      /**
13746      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13747      */
13748     hideTrigger:false,
13749
13750     /**
13751      * @cfg {Boolean} removable (true|false) special filter default false
13752      */
13753     removable : false,
13754     
13755     /** @cfg {Boolean} grow @hide */
13756     /** @cfg {Number} growMin @hide */
13757     /** @cfg {Number} growMax @hide */
13758
13759     /**
13760      * @hide 
13761      * @method
13762      */
13763     autoSize: Roo.emptyFn,
13764     // private
13765     monitorTab : true,
13766     // private
13767     deferHeight : true,
13768
13769     
13770     actionMode : 'wrap',
13771     
13772     caret : false,
13773     
13774     
13775     getAutoCreate : function(){
13776        
13777         var align = this.labelAlign || this.parentLabelAlign();
13778         
13779         var id = Roo.id();
13780         
13781         var cfg = {
13782             cls: 'form-group' //input-group
13783         };
13784         
13785         
13786         var input =  {
13787             tag: 'input',
13788             id : id,
13789             type : this.inputType,
13790             cls : 'form-control',
13791             autocomplete: 'new-password',
13792             placeholder : this.placeholder || '' 
13793             
13794         };
13795         if (this.name) {
13796             input.name = this.name;
13797         }
13798         if (this.size) {
13799             input.cls += ' input-' + this.size;
13800         }
13801         
13802         if (this.disabled) {
13803             input.disabled=true;
13804         }
13805         
13806         var inputblock = input;
13807         
13808         if(this.hasFeedback && !this.allowBlank){
13809             
13810             var feedback = {
13811                 tag: 'span',
13812                 cls: 'glyphicon form-control-feedback'
13813             };
13814             
13815             if(this.removable && !this.editable  ){
13816                 inputblock = {
13817                     cls : 'has-feedback',
13818                     cn :  [
13819                         inputblock,
13820                         {
13821                             tag: 'button',
13822                             html : 'x',
13823                             cls : 'roo-combo-removable-btn close'
13824                         },
13825                         feedback
13826                     ] 
13827                 };
13828             } else {
13829                 inputblock = {
13830                     cls : 'has-feedback',
13831                     cn :  [
13832                         inputblock,
13833                         feedback
13834                     ] 
13835                 };
13836             }
13837
13838         } else {
13839             if(this.removable && !this.editable ){
13840                 inputblock = {
13841                     cls : 'roo-removable',
13842                     cn :  [
13843                         inputblock,
13844                         {
13845                             tag: 'button',
13846                             html : 'x',
13847                             cls : 'roo-combo-removable-btn close'
13848                         }
13849                     ] 
13850                 };
13851             }
13852         }
13853         
13854         if (this.before || this.after) {
13855             
13856             inputblock = {
13857                 cls : 'input-group',
13858                 cn :  [] 
13859             };
13860             if (this.before) {
13861                 inputblock.cn.push({
13862                     tag :'span',
13863                     cls : 'input-group-addon input-group-prepend input-group-text',
13864                     html : this.before
13865                 });
13866             }
13867             
13868             inputblock.cn.push(input);
13869             
13870             if(this.hasFeedback && !this.allowBlank){
13871                 inputblock.cls += ' has-feedback';
13872                 inputblock.cn.push(feedback);
13873             }
13874             
13875             if (this.after) {
13876                 inputblock.cn.push({
13877                     tag :'span',
13878                     cls : 'input-group-addon input-group-append input-group-text',
13879                     html : this.after
13880                 });
13881             }
13882             
13883         };
13884         
13885       
13886         
13887         var ibwrap = inputblock;
13888         
13889         if(this.multiple){
13890             ibwrap = {
13891                 tag: 'ul',
13892                 cls: 'roo-select2-choices',
13893                 cn:[
13894                     {
13895                         tag: 'li',
13896                         cls: 'roo-select2-search-field',
13897                         cn: [
13898
13899                             inputblock
13900                         ]
13901                     }
13902                 ]
13903             };
13904                 
13905         }
13906         
13907         var combobox = {
13908             cls: 'roo-select2-container input-group',
13909             cn: [
13910                  {
13911                     tag: 'input',
13912                     type : 'hidden',
13913                     cls: 'form-hidden-field'
13914                 },
13915                 ibwrap
13916             ]
13917         };
13918         
13919         if(!this.multiple && this.showToggleBtn){
13920             
13921             var caret = {
13922                         tag: 'span',
13923                         cls: 'caret'
13924              };
13925             if (this.caret != false) {
13926                 caret = {
13927                      tag: 'i',
13928                      cls: 'fa fa-' + this.caret
13929                 };
13930                 
13931             }
13932             
13933             combobox.cn.push({
13934                 tag :'span',
13935                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13936                 cn : [
13937                     Roo.bootstrap.version == 3 ? caret : '',
13938                     {
13939                         tag: 'span',
13940                         cls: 'combobox-clear',
13941                         cn  : [
13942                             {
13943                                 tag : 'i',
13944                                 cls: 'icon-remove'
13945                             }
13946                         ]
13947                     }
13948                 ]
13949
13950             })
13951         }
13952         
13953         if(this.multiple){
13954             combobox.cls += ' roo-select2-container-multi';
13955         }
13956          var indicator = {
13957             tag : 'i',
13958             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13959             tooltip : 'This field is required'
13960         };
13961       
13962         if (this.allowBlank) {
13963             indicator = {
13964                 tag : 'i',
13965                 style : 'display:none'
13966             };
13967         }
13968          
13969         
13970         
13971         if (align ==='left' && this.fieldLabel.length) {
13972             
13973             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13974
13975             cfg.cn = [
13976                 indicator,
13977                 {
13978                     tag: 'label',
13979                     'for' :  id,
13980                     cls : 'control-label',
13981                     html : this.fieldLabel
13982
13983                 },
13984                 {
13985                     cls : "", 
13986                     cn: [
13987                         combobox
13988                     ]
13989                 }
13990
13991             ];
13992             
13993             var labelCfg = cfg.cn[1];
13994             var contentCfg = cfg.cn[2];
13995             
13996             if(this.indicatorpos == 'right'){
13997                 cfg.cn = [
13998                     {
13999                         tag: 'label',
14000                         'for' :  id,
14001                         cls : 'control-label',
14002                         cn : [
14003                             {
14004                                 tag : 'span',
14005                                 html : this.fieldLabel
14006                             },
14007                             indicator
14008                         ]
14009                     },
14010                     {
14011                         cls : "", 
14012                         cn: [
14013                             combobox
14014                         ]
14015                     }
14016
14017                 ];
14018                 
14019                 labelCfg = cfg.cn[0];
14020                 contentCfg = cfg.cn[1];
14021             }
14022             
14023             if(this.labelWidth > 12){
14024                 labelCfg.style = "width: " + this.labelWidth + 'px';
14025             }
14026             
14027             if(this.labelWidth < 13 && this.labelmd == 0){
14028                 this.labelmd = this.labelWidth;
14029             }
14030             
14031             if(this.labellg > 0){
14032                 labelCfg.cls += ' col-lg-' + this.labellg;
14033                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14034             }
14035             
14036             if(this.labelmd > 0){
14037                 labelCfg.cls += ' col-md-' + this.labelmd;
14038                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14039             }
14040             
14041             if(this.labelsm > 0){
14042                 labelCfg.cls += ' col-sm-' + this.labelsm;
14043                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14044             }
14045             
14046             if(this.labelxs > 0){
14047                 labelCfg.cls += ' col-xs-' + this.labelxs;
14048                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14049             }
14050             
14051         } else if ( this.fieldLabel.length) {
14052 //                Roo.log(" label");
14053             cfg.cn = [
14054                 indicator,
14055                {
14056                    tag: 'label',
14057                    //cls : 'input-group-addon',
14058                    html : this.fieldLabel
14059
14060                },
14061
14062                combobox
14063
14064             ];
14065             
14066             if(this.indicatorpos == 'right'){
14067                 
14068                 cfg.cn = [
14069                     {
14070                        tag: 'label',
14071                        cn : [
14072                            {
14073                                tag : 'span',
14074                                html : this.fieldLabel
14075                            },
14076                            indicator
14077                        ]
14078
14079                     },
14080                     combobox
14081
14082                 ];
14083
14084             }
14085
14086         } else {
14087             
14088 //                Roo.log(" no label && no align");
14089                 cfg = combobox
14090                      
14091                 
14092         }
14093         
14094         var settings=this;
14095         ['xs','sm','md','lg'].map(function(size){
14096             if (settings[size]) {
14097                 cfg.cls += ' col-' + size + '-' + settings[size];
14098             }
14099         });
14100         
14101         return cfg;
14102         
14103     },
14104     
14105     
14106     
14107     // private
14108     onResize : function(w, h){
14109 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14110 //        if(typeof w == 'number'){
14111 //            var x = w - this.trigger.getWidth();
14112 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14113 //            this.trigger.setStyle('left', x+'px');
14114 //        }
14115     },
14116
14117     // private
14118     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14119
14120     // private
14121     getResizeEl : function(){
14122         return this.inputEl();
14123     },
14124
14125     // private
14126     getPositionEl : function(){
14127         return this.inputEl();
14128     },
14129
14130     // private
14131     alignErrorIcon : function(){
14132         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14133     },
14134
14135     // private
14136     initEvents : function(){
14137         
14138         this.createList();
14139         
14140         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14141         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14142         if(!this.multiple && this.showToggleBtn){
14143             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14144             if(this.hideTrigger){
14145                 this.trigger.setDisplayed(false);
14146             }
14147             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14148         }
14149         
14150         if(this.multiple){
14151             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14152         }
14153         
14154         if(this.removable && !this.editable && !this.tickable){
14155             var close = this.closeTriggerEl();
14156             
14157             if(close){
14158                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14159                 close.on('click', this.removeBtnClick, this, close);
14160             }
14161         }
14162         
14163         //this.trigger.addClassOnOver('x-form-trigger-over');
14164         //this.trigger.addClassOnClick('x-form-trigger-click');
14165         
14166         //if(!this.width){
14167         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14168         //}
14169     },
14170     
14171     closeTriggerEl : function()
14172     {
14173         var close = this.el.select('.roo-combo-removable-btn', true).first();
14174         return close ? close : false;
14175     },
14176     
14177     removeBtnClick : function(e, h, el)
14178     {
14179         e.preventDefault();
14180         
14181         if(this.fireEvent("remove", this) !== false){
14182             this.reset();
14183             this.fireEvent("afterremove", this)
14184         }
14185     },
14186     
14187     createList : function()
14188     {
14189         this.list = Roo.get(document.body).createChild({
14190             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14191             cls: 'typeahead typeahead-long dropdown-menu shadow',
14192             style: 'display:none'
14193         });
14194         
14195         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14196         
14197     },
14198
14199     // private
14200     initTrigger : function(){
14201        
14202     },
14203
14204     // private
14205     onDestroy : function(){
14206         if(this.trigger){
14207             this.trigger.removeAllListeners();
14208           //  this.trigger.remove();
14209         }
14210         //if(this.wrap){
14211         //    this.wrap.remove();
14212         //}
14213         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14214     },
14215
14216     // private
14217     onFocus : function(){
14218         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14219         /*
14220         if(!this.mimicing){
14221             this.wrap.addClass('x-trigger-wrap-focus');
14222             this.mimicing = true;
14223             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14224             if(this.monitorTab){
14225                 this.el.on("keydown", this.checkTab, this);
14226             }
14227         }
14228         */
14229     },
14230
14231     // private
14232     checkTab : function(e){
14233         if(e.getKey() == e.TAB){
14234             this.triggerBlur();
14235         }
14236     },
14237
14238     // private
14239     onBlur : function(){
14240         // do nothing
14241     },
14242
14243     // private
14244     mimicBlur : function(e, t){
14245         /*
14246         if(!this.wrap.contains(t) && this.validateBlur()){
14247             this.triggerBlur();
14248         }
14249         */
14250     },
14251
14252     // private
14253     triggerBlur : function(){
14254         this.mimicing = false;
14255         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14256         if(this.monitorTab){
14257             this.el.un("keydown", this.checkTab, this);
14258         }
14259         //this.wrap.removeClass('x-trigger-wrap-focus');
14260         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14261     },
14262
14263     // private
14264     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14265     validateBlur : function(e, t){
14266         return true;
14267     },
14268
14269     // private
14270     onDisable : function(){
14271         this.inputEl().dom.disabled = true;
14272         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14273         //if(this.wrap){
14274         //    this.wrap.addClass('x-item-disabled');
14275         //}
14276     },
14277
14278     // private
14279     onEnable : function(){
14280         this.inputEl().dom.disabled = false;
14281         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14282         //if(this.wrap){
14283         //    this.el.removeClass('x-item-disabled');
14284         //}
14285     },
14286
14287     // private
14288     onShow : function(){
14289         var ae = this.getActionEl();
14290         
14291         if(ae){
14292             ae.dom.style.display = '';
14293             ae.dom.style.visibility = 'visible';
14294         }
14295     },
14296
14297     // private
14298     
14299     onHide : function(){
14300         var ae = this.getActionEl();
14301         ae.dom.style.display = 'none';
14302     },
14303
14304     /**
14305      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14306      * by an implementing function.
14307      * @method
14308      * @param {EventObject} e
14309      */
14310     onTriggerClick : Roo.emptyFn
14311 });
14312  
14313 /*
14314 * Licence: LGPL
14315 */
14316
14317 /**
14318  * @class Roo.bootstrap.form.CardUploader
14319  * @extends Roo.bootstrap.Button
14320  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14321  * @cfg {Number} errorTimeout default 3000
14322  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14323  * @cfg {Array}  html The button text.
14324
14325  *
14326  * @constructor
14327  * Create a new CardUploader
14328  * @param {Object} config The config object
14329  */
14330
14331 Roo.bootstrap.form.CardUploader = function(config){
14332     
14333  
14334     
14335     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14336     
14337     
14338     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14339         return r.data.id
14340      });
14341     
14342      this.addEvents({
14343          // raw events
14344         /**
14345          * @event preview
14346          * When a image is clicked on - and needs to display a slideshow or similar..
14347          * @param {Roo.bootstrap.Card} this
14348          * @param {Object} The image information data 
14349          *
14350          */
14351         'preview' : true,
14352          /**
14353          * @event download
14354          * When a the download link is clicked
14355          * @param {Roo.bootstrap.Card} this
14356          * @param {Object} The image information data  contains 
14357          */
14358         'download' : true
14359         
14360     });
14361 };
14362  
14363 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14364     
14365      
14366     errorTimeout : 3000,
14367      
14368     images : false,
14369    
14370     fileCollection : false,
14371     allowBlank : true,
14372     
14373     getAutoCreate : function()
14374     {
14375         
14376         var cfg =  {
14377             cls :'form-group' ,
14378             cn : [
14379                
14380                 {
14381                     tag: 'label',
14382                    //cls : 'input-group-addon',
14383                     html : this.fieldLabel
14384
14385                 },
14386
14387                 {
14388                     tag: 'input',
14389                     type : 'hidden',
14390                     name : this.name,
14391                     value : this.value,
14392                     cls : 'd-none  form-control'
14393                 },
14394                 
14395                 {
14396                     tag: 'input',
14397                     multiple : 'multiple',
14398                     type : 'file',
14399                     cls : 'd-none  roo-card-upload-selector'
14400                 },
14401                 
14402                 {
14403                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14404                 },
14405                 {
14406                     cls : 'card-columns roo-card-uploader-container'
14407                 }
14408
14409             ]
14410         };
14411            
14412          
14413         return cfg;
14414     },
14415     
14416     getChildContainer : function() /// what children are added to.
14417     {
14418         return this.containerEl;
14419     },
14420    
14421     getButtonContainer : function() /// what children are added to.
14422     {
14423         return this.el.select(".roo-card-uploader-button-container").first();
14424     },
14425    
14426     initEvents : function()
14427     {
14428         
14429         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14430         
14431         var t = this;
14432         this.addxtype({
14433             xns: Roo.bootstrap,
14434
14435             xtype : 'Button',
14436             container_method : 'getButtonContainer' ,            
14437             html :  this.html, // fix changable?
14438             cls : 'w-100 ',
14439             listeners : {
14440                 'click' : function(btn, e) {
14441                     t.onClick(e);
14442                 }
14443             }
14444         });
14445         
14446         
14447         
14448         
14449         this.urlAPI = (window.createObjectURL && window) || 
14450                                 (window.URL && URL.revokeObjectURL && URL) || 
14451                                 (window.webkitURL && webkitURL);
14452                         
14453          
14454          
14455          
14456         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14457         
14458         this.selectorEl.on('change', this.onFileSelected, this);
14459         if (this.images) {
14460             var t = this;
14461             this.images.forEach(function(img) {
14462                 t.addCard(img)
14463             });
14464             this.images = false;
14465         }
14466         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14467          
14468        
14469     },
14470     
14471    
14472     onClick : function(e)
14473     {
14474         e.preventDefault();
14475          
14476         this.selectorEl.dom.click();
14477          
14478     },
14479     
14480     onFileSelected : function(e)
14481     {
14482         e.preventDefault();
14483         
14484         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14485             return;
14486         }
14487         
14488         Roo.each(this.selectorEl.dom.files, function(file){    
14489             this.addFile(file);
14490         }, this);
14491          
14492     },
14493     
14494       
14495     
14496       
14497     
14498     addFile : function(file)
14499     {
14500            
14501         if(typeof(file) === 'string'){
14502             throw "Add file by name?"; // should not happen
14503             return;
14504         }
14505         
14506         if(!file || !this.urlAPI){
14507             return;
14508         }
14509         
14510         // file;
14511         // file.type;
14512         
14513         var _this = this;
14514         
14515         
14516         var url = _this.urlAPI.createObjectURL( file);
14517            
14518         this.addCard({
14519             id : Roo.bootstrap.form.CardUploader.ID--,
14520             is_uploaded : false,
14521             src : url,
14522             srcfile : file,
14523             title : file.name,
14524             mimetype : file.type,
14525             preview : false,
14526             is_deleted : 0
14527         });
14528         
14529     },
14530     
14531     /**
14532      * addCard - add an Attachment to the uploader
14533      * @param data - the data about the image to upload
14534      *
14535      * {
14536           id : 123
14537           title : "Title of file",
14538           is_uploaded : false,
14539           src : "http://.....",
14540           srcfile : { the File upload object },
14541           mimetype : file.type,
14542           preview : false,
14543           is_deleted : 0
14544           .. any other data...
14545         }
14546      *
14547      * 
14548     */
14549     
14550     addCard : function (data)
14551     {
14552         // hidden input element?
14553         // if the file is not an image...
14554         //then we need to use something other that and header_image
14555         var t = this;
14556         //   remove.....
14557         var footer = [
14558             {
14559                 xns : Roo.bootstrap,
14560                 xtype : 'CardFooter',
14561                  items: [
14562                     {
14563                         xns : Roo.bootstrap,
14564                         xtype : 'Element',
14565                         cls : 'd-flex',
14566                         items : [
14567                             
14568                             {
14569                                 xns : Roo.bootstrap,
14570                                 xtype : 'Button',
14571                                 html : String.format("<small>{0}</small>", data.title),
14572                                 cls : 'col-10 text-left',
14573                                 size: 'sm',
14574                                 weight: 'link',
14575                                 fa : 'download',
14576                                 listeners : {
14577                                     click : function() {
14578                                      
14579                                         t.fireEvent( "download", t, data );
14580                                     }
14581                                 }
14582                             },
14583                           
14584                             {
14585                                 xns : Roo.bootstrap,
14586                                 xtype : 'Button',
14587                                 style: 'max-height: 28px; ',
14588                                 size : 'sm',
14589                                 weight: 'danger',
14590                                 cls : 'col-2',
14591                                 fa : 'times',
14592                                 listeners : {
14593                                     click : function() {
14594                                         t.removeCard(data.id)
14595                                     }
14596                                 }
14597                             }
14598                         ]
14599                     }
14600                     
14601                 ] 
14602             }
14603             
14604         ];
14605         
14606         var cn = this.addxtype(
14607             {
14608                  
14609                 xns : Roo.bootstrap,
14610                 xtype : 'Card',
14611                 closeable : true,
14612                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14613                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14614                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14615                 data : data,
14616                 html : false,
14617                  
14618                 items : footer,
14619                 initEvents : function() {
14620                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14621                     var card = this;
14622                     this.imgEl = this.el.select('.card-img-top').first();
14623                     if (this.imgEl) {
14624                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14625                         this.imgEl.set({ 'pointer' : 'cursor' });
14626                                   
14627                     }
14628                     this.getCardFooter().addClass('p-1');
14629                     
14630                   
14631                 }
14632                 
14633             }
14634         );
14635         // dont' really need ot update items.
14636         // this.items.push(cn);
14637         this.fileCollection.add(cn);
14638         
14639         if (!data.srcfile) {
14640             this.updateInput();
14641             return;
14642         }
14643             
14644         var _t = this;
14645         var reader = new FileReader();
14646         reader.addEventListener("load", function() {  
14647             data.srcdata =  reader.result;
14648             _t.updateInput();
14649         });
14650         reader.readAsDataURL(data.srcfile);
14651         
14652         
14653         
14654     },
14655     removeCard : function(id)
14656     {
14657         
14658         var card  = this.fileCollection.get(id);
14659         card.data.is_deleted = 1;
14660         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14661         //this.fileCollection.remove(card);
14662         //this.items = this.items.filter(function(e) { return e != card });
14663         // dont' really need ot update items.
14664         card.el.dom.parentNode.removeChild(card.el.dom);
14665         this.updateInput();
14666
14667         
14668     },
14669     reset: function()
14670     {
14671         this.fileCollection.each(function(card) {
14672             if (card.el.dom && card.el.dom.parentNode) {
14673                 card.el.dom.parentNode.removeChild(card.el.dom);
14674             }
14675         });
14676         this.fileCollection.clear();
14677         this.updateInput();
14678     },
14679     
14680     updateInput : function()
14681     {
14682          var data = [];
14683         this.fileCollection.each(function(e) {
14684             data.push(e.data);
14685             
14686         });
14687         this.inputEl().dom.value = JSON.stringify(data);
14688         
14689         
14690         
14691     }
14692     
14693     
14694 });
14695
14696
14697 Roo.bootstrap.form.CardUploader.ID = -1;/*
14698  * Based on:
14699  * Ext JS Library 1.1.1
14700  * Copyright(c) 2006-2007, Ext JS, LLC.
14701  *
14702  * Originally Released Under LGPL - original licence link has changed is not relivant.
14703  *
14704  * Fork - LGPL
14705  * <script type="text/javascript">
14706  */
14707
14708
14709 /**
14710  * @class Roo.data.SortTypes
14711  * @static
14712  * Defines the default sorting (casting?) comparison functions used when sorting data.
14713  */
14714 Roo.data.SortTypes = {
14715     /**
14716      * Default sort that does nothing
14717      * @param {Mixed} s The value being converted
14718      * @return {Mixed} The comparison value
14719      */
14720     none : function(s){
14721         return s;
14722     },
14723     
14724     /**
14725      * The regular expression used to strip tags
14726      * @type {RegExp}
14727      * @property
14728      */
14729     stripTagsRE : /<\/?[^>]+>/gi,
14730     
14731     /**
14732      * Strips all HTML tags to sort on text only
14733      * @param {Mixed} s The value being converted
14734      * @return {String} The comparison value
14735      */
14736     asText : function(s){
14737         return String(s).replace(this.stripTagsRE, "");
14738     },
14739     
14740     /**
14741      * Strips all HTML tags to sort on text only - Case insensitive
14742      * @param {Mixed} s The value being converted
14743      * @return {String} The comparison value
14744      */
14745     asUCText : function(s){
14746         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14747     },
14748     
14749     /**
14750      * Case insensitive string
14751      * @param {Mixed} s The value being converted
14752      * @return {String} The comparison value
14753      */
14754     asUCString : function(s) {
14755         return String(s).toUpperCase();
14756     },
14757     
14758     /**
14759      * Date sorting
14760      * @param {Mixed} s The value being converted
14761      * @return {Number} The comparison value
14762      */
14763     asDate : function(s) {
14764         if(!s){
14765             return 0;
14766         }
14767         if(s instanceof Date){
14768             return s.getTime();
14769         }
14770         return Date.parse(String(s));
14771     },
14772     
14773     /**
14774      * Float sorting
14775      * @param {Mixed} s The value being converted
14776      * @return {Float} The comparison value
14777      */
14778     asFloat : function(s) {
14779         var val = parseFloat(String(s).replace(/,/g, ""));
14780         if(isNaN(val)) {
14781             val = 0;
14782         }
14783         return val;
14784     },
14785     
14786     /**
14787      * Integer sorting
14788      * @param {Mixed} s The value being converted
14789      * @return {Number} The comparison value
14790      */
14791     asInt : function(s) {
14792         var val = parseInt(String(s).replace(/,/g, ""));
14793         if(isNaN(val)) {
14794             val = 0;
14795         }
14796         return val;
14797     }
14798 };/*
14799  * Based on:
14800  * Ext JS Library 1.1.1
14801  * Copyright(c) 2006-2007, Ext JS, LLC.
14802  *
14803  * Originally Released Under LGPL - original licence link has changed is not relivant.
14804  *
14805  * Fork - LGPL
14806  * <script type="text/javascript">
14807  */
14808
14809 /**
14810 * @class Roo.data.Record
14811  * Instances of this class encapsulate both record <em>definition</em> information, and record
14812  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14813  * to access Records cached in an {@link Roo.data.Store} object.<br>
14814  * <p>
14815  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14816  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14817  * objects.<br>
14818  * <p>
14819  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14820  * @constructor
14821  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14822  * {@link #create}. The parameters are the same.
14823  * @param {Array} data An associative Array of data values keyed by the field name.
14824  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14825  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14826  * not specified an integer id is generated.
14827  */
14828 Roo.data.Record = function(data, id){
14829     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14830     this.data = data;
14831 };
14832
14833 /**
14834  * Generate a constructor for a specific record layout.
14835  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14836  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14837  * Each field definition object may contain the following properties: <ul>
14838  * <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,
14839  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14840  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14841  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14842  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14843  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14844  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14845  * this may be omitted.</p></li>
14846  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14847  * <ul><li>auto (Default, implies no conversion)</li>
14848  * <li>string</li>
14849  * <li>int</li>
14850  * <li>float</li>
14851  * <li>boolean</li>
14852  * <li>date</li></ul></p></li>
14853  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14854  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14855  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14856  * by the Reader into an object that will be stored in the Record. It is passed the
14857  * following parameters:<ul>
14858  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14859  * </ul></p></li>
14860  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14861  * </ul>
14862  * <br>usage:<br><pre><code>
14863 var TopicRecord = Roo.data.Record.create(
14864     {name: 'title', mapping: 'topic_title'},
14865     {name: 'author', mapping: 'username'},
14866     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14867     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14868     {name: 'lastPoster', mapping: 'user2'},
14869     {name: 'excerpt', mapping: 'post_text'}
14870 );
14871
14872 var myNewRecord = new TopicRecord({
14873     title: 'Do my job please',
14874     author: 'noobie',
14875     totalPosts: 1,
14876     lastPost: new Date(),
14877     lastPoster: 'Animal',
14878     excerpt: 'No way dude!'
14879 });
14880 myStore.add(myNewRecord);
14881 </code></pre>
14882  * @method create
14883  * @static
14884  */
14885 Roo.data.Record.create = function(o){
14886     var f = function(){
14887         f.superclass.constructor.apply(this, arguments);
14888     };
14889     Roo.extend(f, Roo.data.Record);
14890     var p = f.prototype;
14891     p.fields = new Roo.util.MixedCollection(false, function(field){
14892         return field.name;
14893     });
14894     for(var i = 0, len = o.length; i < len; i++){
14895         p.fields.add(new Roo.data.Field(o[i]));
14896     }
14897     f.getField = function(name){
14898         return p.fields.get(name);  
14899     };
14900     return f;
14901 };
14902
14903 Roo.data.Record.AUTO_ID = 1000;
14904 Roo.data.Record.EDIT = 'edit';
14905 Roo.data.Record.REJECT = 'reject';
14906 Roo.data.Record.COMMIT = 'commit';
14907
14908 Roo.data.Record.prototype = {
14909     /**
14910      * Readonly flag - true if this record has been modified.
14911      * @type Boolean
14912      */
14913     dirty : false,
14914     editing : false,
14915     error: null,
14916     modified: null,
14917
14918     // private
14919     join : function(store){
14920         this.store = store;
14921     },
14922
14923     /**
14924      * Set the named field to the specified value.
14925      * @param {String} name The name of the field to set.
14926      * @param {Object} value The value to set the field to.
14927      */
14928     set : function(name, value){
14929         if(this.data[name] == value){
14930             return;
14931         }
14932         this.dirty = true;
14933         if(!this.modified){
14934             this.modified = {};
14935         }
14936         if(typeof this.modified[name] == 'undefined'){
14937             this.modified[name] = this.data[name];
14938         }
14939         this.data[name] = value;
14940         if(!this.editing && this.store){
14941             this.store.afterEdit(this);
14942         }       
14943     },
14944
14945     /**
14946      * Get the value of the named field.
14947      * @param {String} name The name of the field to get the value of.
14948      * @return {Object} The value of the field.
14949      */
14950     get : function(name){
14951         return this.data[name]; 
14952     },
14953
14954     // private
14955     beginEdit : function(){
14956         this.editing = true;
14957         this.modified = {}; 
14958     },
14959
14960     // private
14961     cancelEdit : function(){
14962         this.editing = false;
14963         delete this.modified;
14964     },
14965
14966     // private
14967     endEdit : function(){
14968         this.editing = false;
14969         if(this.dirty && this.store){
14970             this.store.afterEdit(this);
14971         }
14972     },
14973
14974     /**
14975      * Usually called by the {@link Roo.data.Store} which owns the Record.
14976      * Rejects all changes made to the Record since either creation, or the last commit operation.
14977      * Modified fields are reverted to their original values.
14978      * <p>
14979      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14980      * of reject operations.
14981      */
14982     reject : function(){
14983         var m = this.modified;
14984         for(var n in m){
14985             if(typeof m[n] != "function"){
14986                 this.data[n] = m[n];
14987             }
14988         }
14989         this.dirty = false;
14990         delete this.modified;
14991         this.editing = false;
14992         if(this.store){
14993             this.store.afterReject(this);
14994         }
14995     },
14996
14997     /**
14998      * Usually called by the {@link Roo.data.Store} which owns the Record.
14999      * Commits all changes made to the Record since either creation, or the last commit operation.
15000      * <p>
15001      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15002      * of commit operations.
15003      */
15004     commit : function(){
15005         this.dirty = false;
15006         delete this.modified;
15007         this.editing = false;
15008         if(this.store){
15009             this.store.afterCommit(this);
15010         }
15011     },
15012
15013     // private
15014     hasError : function(){
15015         return this.error != null;
15016     },
15017
15018     // private
15019     clearError : function(){
15020         this.error = null;
15021     },
15022
15023     /**
15024      * Creates a copy of this record.
15025      * @param {String} id (optional) A new record id if you don't want to use this record's id
15026      * @return {Record}
15027      */
15028     copy : function(newId) {
15029         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15030     }
15031 };/*
15032  * Based on:
15033  * Ext JS Library 1.1.1
15034  * Copyright(c) 2006-2007, Ext JS, LLC.
15035  *
15036  * Originally Released Under LGPL - original licence link has changed is not relivant.
15037  *
15038  * Fork - LGPL
15039  * <script type="text/javascript">
15040  */
15041
15042
15043
15044 /**
15045  * @class Roo.data.Store
15046  * @extends Roo.util.Observable
15047  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15048  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15049  * <p>
15050  * 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
15051  * has no knowledge of the format of the data returned by the Proxy.<br>
15052  * <p>
15053  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15054  * instances from the data object. These records are cached and made available through accessor functions.
15055  * @constructor
15056  * Creates a new Store.
15057  * @param {Object} config A config object containing the objects needed for the Store to access data,
15058  * and read the data into Records.
15059  */
15060 Roo.data.Store = function(config){
15061     this.data = new Roo.util.MixedCollection(false);
15062     this.data.getKey = function(o){
15063         return o.id;
15064     };
15065     this.baseParams = {};
15066     // private
15067     this.paramNames = {
15068         "start" : "start",
15069         "limit" : "limit",
15070         "sort" : "sort",
15071         "dir" : "dir",
15072         "multisort" : "_multisort"
15073     };
15074
15075     if(config && config.data){
15076         this.inlineData = config.data;
15077         delete config.data;
15078     }
15079
15080     Roo.apply(this, config);
15081     
15082     if(this.reader){ // reader passed
15083         this.reader = Roo.factory(this.reader, Roo.data);
15084         this.reader.xmodule = this.xmodule || false;
15085         if(!this.recordType){
15086             this.recordType = this.reader.recordType;
15087         }
15088         if(this.reader.onMetaChange){
15089             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15090         }
15091     }
15092
15093     if(this.recordType){
15094         this.fields = this.recordType.prototype.fields;
15095     }
15096     this.modified = [];
15097
15098     this.addEvents({
15099         /**
15100          * @event datachanged
15101          * Fires when the data cache has changed, and a widget which is using this Store
15102          * as a Record cache should refresh its view.
15103          * @param {Store} this
15104          */
15105         datachanged : true,
15106         /**
15107          * @event metachange
15108          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15109          * @param {Store} this
15110          * @param {Object} meta The JSON metadata
15111          */
15112         metachange : true,
15113         /**
15114          * @event add
15115          * Fires when Records have been added to the Store
15116          * @param {Store} this
15117          * @param {Roo.data.Record[]} records The array of Records added
15118          * @param {Number} index The index at which the record(s) were added
15119          */
15120         add : true,
15121         /**
15122          * @event remove
15123          * Fires when a Record has been removed from the Store
15124          * @param {Store} this
15125          * @param {Roo.data.Record} record The Record that was removed
15126          * @param {Number} index The index at which the record was removed
15127          */
15128         remove : true,
15129         /**
15130          * @event update
15131          * Fires when a Record has been updated
15132          * @param {Store} this
15133          * @param {Roo.data.Record} record The Record that was updated
15134          * @param {String} operation The update operation being performed.  Value may be one of:
15135          * <pre><code>
15136  Roo.data.Record.EDIT
15137  Roo.data.Record.REJECT
15138  Roo.data.Record.COMMIT
15139          * </code></pre>
15140          */
15141         update : true,
15142         /**
15143          * @event clear
15144          * Fires when the data cache has been cleared.
15145          * @param {Store} this
15146          */
15147         clear : true,
15148         /**
15149          * @event beforeload
15150          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15151          * the load action will be canceled.
15152          * @param {Store} this
15153          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15154          */
15155         beforeload : true,
15156         /**
15157          * @event beforeloadadd
15158          * Fires after a new set of Records has been loaded.
15159          * @param {Store} this
15160          * @param {Roo.data.Record[]} records The Records that were loaded
15161          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15162          */
15163         beforeloadadd : true,
15164         /**
15165          * @event load
15166          * Fires after a new set of Records has been loaded, before they are added to the store.
15167          * @param {Store} this
15168          * @param {Roo.data.Record[]} records The Records that were loaded
15169          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15170          * @params {Object} return from reader
15171          */
15172         load : true,
15173         /**
15174          * @event loadexception
15175          * Fires if an exception occurs in the Proxy during loading.
15176          * Called with the signature of the Proxy's "loadexception" event.
15177          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15178          * 
15179          * @param {Proxy} 
15180          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15181          * @param {Object} opts - load Options
15182          * @param {Object} jsonData from your request (normally this contains the Exception)
15183          */
15184         loadexception : true
15185     });
15186     
15187     if(this.proxy){
15188         this.proxy = Roo.factory(this.proxy, Roo.data);
15189         this.proxy.xmodule = this.xmodule || false;
15190         this.relayEvents(this.proxy,  ["loadexception"]);
15191     }
15192     this.sortToggle = {};
15193     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15194
15195     Roo.data.Store.superclass.constructor.call(this);
15196
15197     if(this.inlineData){
15198         this.loadData(this.inlineData);
15199         delete this.inlineData;
15200     }
15201 };
15202
15203 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15204      /**
15205     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15206     * without a remote query - used by combo/forms at present.
15207     */
15208     
15209     /**
15210     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15211     */
15212     /**
15213     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15214     */
15215     /**
15216     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15217     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15218     */
15219     /**
15220     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15221     * on any HTTP request
15222     */
15223     /**
15224     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15225     */
15226     /**
15227     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15228     */
15229     multiSort: false,
15230     /**
15231     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15232     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15233     */
15234     remoteSort : false,
15235
15236     /**
15237     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15238      * loaded or when a record is removed. (defaults to false).
15239     */
15240     pruneModifiedRecords : false,
15241
15242     // private
15243     lastOptions : null,
15244
15245     /**
15246      * Add Records to the Store and fires the add event.
15247      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15248      */
15249     add : function(records){
15250         records = [].concat(records);
15251         for(var i = 0, len = records.length; i < len; i++){
15252             records[i].join(this);
15253         }
15254         var index = this.data.length;
15255         this.data.addAll(records);
15256         this.fireEvent("add", this, records, index);
15257     },
15258
15259     /**
15260      * Remove a Record from the Store and fires the remove event.
15261      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15262      */
15263     remove : function(record){
15264         var index = this.data.indexOf(record);
15265         this.data.removeAt(index);
15266  
15267         if(this.pruneModifiedRecords){
15268             this.modified.remove(record);
15269         }
15270         this.fireEvent("remove", this, record, index);
15271     },
15272
15273     /**
15274      * Remove all Records from the Store and fires the clear event.
15275      */
15276     removeAll : function(){
15277         this.data.clear();
15278         if(this.pruneModifiedRecords){
15279             this.modified = [];
15280         }
15281         this.fireEvent("clear", this);
15282     },
15283
15284     /**
15285      * Inserts Records to the Store at the given index and fires the add event.
15286      * @param {Number} index The start index at which to insert the passed Records.
15287      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15288      */
15289     insert : function(index, records){
15290         records = [].concat(records);
15291         for(var i = 0, len = records.length; i < len; i++){
15292             this.data.insert(index, records[i]);
15293             records[i].join(this);
15294         }
15295         this.fireEvent("add", this, records, index);
15296     },
15297
15298     /**
15299      * Get the index within the cache of the passed Record.
15300      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15301      * @return {Number} The index of the passed Record. Returns -1 if not found.
15302      */
15303     indexOf : function(record){
15304         return this.data.indexOf(record);
15305     },
15306
15307     /**
15308      * Get the index within the cache of the Record with the passed id.
15309      * @param {String} id The id of the Record to find.
15310      * @return {Number} The index of the Record. Returns -1 if not found.
15311      */
15312     indexOfId : function(id){
15313         return this.data.indexOfKey(id);
15314     },
15315
15316     /**
15317      * Get the Record with the specified id.
15318      * @param {String} id The id of the Record to find.
15319      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15320      */
15321     getById : function(id){
15322         return this.data.key(id);
15323     },
15324
15325     /**
15326      * Get the Record at the specified index.
15327      * @param {Number} index The index of the Record to find.
15328      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15329      */
15330     getAt : function(index){
15331         return this.data.itemAt(index);
15332     },
15333
15334     /**
15335      * Returns a range of Records between specified indices.
15336      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15337      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15338      * @return {Roo.data.Record[]} An array of Records
15339      */
15340     getRange : function(start, end){
15341         return this.data.getRange(start, end);
15342     },
15343
15344     // private
15345     storeOptions : function(o){
15346         o = Roo.apply({}, o);
15347         delete o.callback;
15348         delete o.scope;
15349         this.lastOptions = o;
15350     },
15351
15352     /**
15353      * Loads the Record cache from the configured Proxy using the configured Reader.
15354      * <p>
15355      * If using remote paging, then the first load call must specify the <em>start</em>
15356      * and <em>limit</em> properties in the options.params property to establish the initial
15357      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15358      * <p>
15359      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15360      * and this call will return before the new data has been loaded. Perform any post-processing
15361      * in a callback function, or in a "load" event handler.</strong>
15362      * <p>
15363      * @param {Object} options An object containing properties which control loading options:<ul>
15364      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15365      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15366      * <pre>
15367                 {
15368                     data : data,  // array of key=>value data like JsonReader
15369                     total : data.length,
15370                     success : true
15371                     
15372                 }
15373         </pre>
15374             }.</li>
15375      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15376      * passed the following arguments:<ul>
15377      * <li>r : Roo.data.Record[]</li>
15378      * <li>options: Options object from the load call</li>
15379      * <li>success: Boolean success indicator</li></ul></li>
15380      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15381      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15382      * </ul>
15383      */
15384     load : function(options){
15385         options = options || {};
15386         if(this.fireEvent("beforeload", this, options) !== false){
15387             this.storeOptions(options);
15388             var p = Roo.apply(options.params || {}, this.baseParams);
15389             // if meta was not loaded from remote source.. try requesting it.
15390             if (!this.reader.metaFromRemote) {
15391                 p._requestMeta = 1;
15392             }
15393             if(this.sortInfo && this.remoteSort){
15394                 var pn = this.paramNames;
15395                 p[pn["sort"]] = this.sortInfo.field;
15396                 p[pn["dir"]] = this.sortInfo.direction;
15397             }
15398             if (this.multiSort) {
15399                 var pn = this.paramNames;
15400                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15401             }
15402             
15403             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15404         }
15405     },
15406
15407     /**
15408      * Reloads the Record cache from the configured Proxy using the configured Reader and
15409      * the options from the last load operation performed.
15410      * @param {Object} options (optional) An object containing properties which may override the options
15411      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15412      * the most recently used options are reused).
15413      */
15414     reload : function(options){
15415         this.load(Roo.applyIf(options||{}, this.lastOptions));
15416     },
15417
15418     // private
15419     // Called as a callback by the Reader during a load operation.
15420     loadRecords : function(o, options, success){
15421          
15422         if(!o){
15423             if(success !== false){
15424                 this.fireEvent("load", this, [], options, o);
15425             }
15426             if(options.callback){
15427                 options.callback.call(options.scope || this, [], options, false);
15428             }
15429             return;
15430         }
15431         // if data returned failure - throw an exception.
15432         if (o.success === false) {
15433             // show a message if no listener is registered.
15434             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15435                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15436             }
15437             // loadmask wil be hooked into this..
15438             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15439             return;
15440         }
15441         var r = o.records, t = o.totalRecords || r.length;
15442         
15443         this.fireEvent("beforeloadadd", this, r, options, o);
15444         
15445         if(!options || options.add !== true){
15446             if(this.pruneModifiedRecords){
15447                 this.modified = [];
15448             }
15449             for(var i = 0, len = r.length; i < len; i++){
15450                 r[i].join(this);
15451             }
15452             if(this.snapshot){
15453                 this.data = this.snapshot;
15454                 delete this.snapshot;
15455             }
15456             this.data.clear();
15457             this.data.addAll(r);
15458             this.totalLength = t;
15459             this.applySort();
15460             this.fireEvent("datachanged", this);
15461         }else{
15462             this.totalLength = Math.max(t, this.data.length+r.length);
15463             this.add(r);
15464         }
15465         
15466         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15467                 
15468             var e = new Roo.data.Record({});
15469
15470             e.set(this.parent.displayField, this.parent.emptyTitle);
15471             e.set(this.parent.valueField, '');
15472
15473             this.insert(0, e);
15474         }
15475             
15476         this.fireEvent("load", this, r, options, o);
15477         if(options.callback){
15478             options.callback.call(options.scope || this, r, options, true);
15479         }
15480     },
15481
15482
15483     /**
15484      * Loads data from a passed data block. A Reader which understands the format of the data
15485      * must have been configured in the constructor.
15486      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15487      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15488      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15489      */
15490     loadData : function(o, append){
15491         var r = this.reader.readRecords(o);
15492         this.loadRecords(r, {add: append}, true);
15493     },
15494     
15495      /**
15496      * using 'cn' the nested child reader read the child array into it's child stores.
15497      * @param {Object} rec The record with a 'children array
15498      */
15499     loadDataFromChildren : function(rec)
15500     {
15501         this.loadData(this.reader.toLoadData(rec));
15502     },
15503     
15504
15505     /**
15506      * Gets the number of cached records.
15507      * <p>
15508      * <em>If using paging, this may not be the total size of the dataset. If the data object
15509      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15510      * the data set size</em>
15511      */
15512     getCount : function(){
15513         return this.data.length || 0;
15514     },
15515
15516     /**
15517      * Gets the total number of records in the dataset as returned by the server.
15518      * <p>
15519      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15520      * the dataset size</em>
15521      */
15522     getTotalCount : function(){
15523         return this.totalLength || 0;
15524     },
15525
15526     /**
15527      * Returns the sort state of the Store as an object with two properties:
15528      * <pre><code>
15529  field {String} The name of the field by which the Records are sorted
15530  direction {String} The sort order, "ASC" or "DESC"
15531      * </code></pre>
15532      */
15533     getSortState : function(){
15534         return this.sortInfo;
15535     },
15536
15537     // private
15538     applySort : function(){
15539         if(this.sortInfo && !this.remoteSort){
15540             var s = this.sortInfo, f = s.field;
15541             var st = this.fields.get(f).sortType;
15542             var fn = function(r1, r2){
15543                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15544                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15545             };
15546             this.data.sort(s.direction, fn);
15547             if(this.snapshot && this.snapshot != this.data){
15548                 this.snapshot.sort(s.direction, fn);
15549             }
15550         }
15551     },
15552
15553     /**
15554      * Sets the default sort column and order to be used by the next load operation.
15555      * @param {String} fieldName The name of the field to sort by.
15556      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15557      */
15558     setDefaultSort : function(field, dir){
15559         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15560     },
15561
15562     /**
15563      * Sort the Records.
15564      * If remote sorting is used, the sort is performed on the server, and the cache is
15565      * reloaded. If local sorting is used, the cache is sorted internally.
15566      * @param {String} fieldName The name of the field to sort by.
15567      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15568      */
15569     sort : function(fieldName, dir){
15570         var f = this.fields.get(fieldName);
15571         if(!dir){
15572             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15573             
15574             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15575                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15576             }else{
15577                 dir = f.sortDir;
15578             }
15579         }
15580         this.sortToggle[f.name] = dir;
15581         this.sortInfo = {field: f.name, direction: dir};
15582         if(!this.remoteSort){
15583             this.applySort();
15584             this.fireEvent("datachanged", this);
15585         }else{
15586             this.load(this.lastOptions);
15587         }
15588     },
15589
15590     /**
15591      * Calls the specified function for each of the Records in the cache.
15592      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15593      * Returning <em>false</em> aborts and exits the iteration.
15594      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15595      */
15596     each : function(fn, scope){
15597         this.data.each(fn, scope);
15598     },
15599
15600     /**
15601      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15602      * (e.g., during paging).
15603      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15604      */
15605     getModifiedRecords : function(){
15606         return this.modified;
15607     },
15608
15609     // private
15610     createFilterFn : function(property, value, anyMatch){
15611         if(!value.exec){ // not a regex
15612             value = String(value);
15613             if(value.length == 0){
15614                 return false;
15615             }
15616             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15617         }
15618         return function(r){
15619             return value.test(r.data[property]);
15620         };
15621     },
15622
15623     /**
15624      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15625      * @param {String} property A field on your records
15626      * @param {Number} start The record index to start at (defaults to 0)
15627      * @param {Number} end The last record index to include (defaults to length - 1)
15628      * @return {Number} The sum
15629      */
15630     sum : function(property, start, end){
15631         var rs = this.data.items, v = 0;
15632         start = start || 0;
15633         end = (end || end === 0) ? end : rs.length-1;
15634
15635         for(var i = start; i <= end; i++){
15636             v += (rs[i].data[property] || 0);
15637         }
15638         return v;
15639     },
15640
15641     /**
15642      * Filter the records by a specified property.
15643      * @param {String} field A field on your records
15644      * @param {String/RegExp} value Either a string that the field
15645      * should start with or a RegExp to test against the field
15646      * @param {Boolean} anyMatch True to match any part not just the beginning
15647      */
15648     filter : function(property, value, anyMatch){
15649         var fn = this.createFilterFn(property, value, anyMatch);
15650         return fn ? this.filterBy(fn) : this.clearFilter();
15651     },
15652
15653     /**
15654      * Filter by a function. The specified function will be called with each
15655      * record in this data source. If the function returns true the record is included,
15656      * otherwise it is filtered.
15657      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15658      * @param {Object} scope (optional) The scope of the function (defaults to this)
15659      */
15660     filterBy : function(fn, scope){
15661         this.snapshot = this.snapshot || this.data;
15662         this.data = this.queryBy(fn, scope||this);
15663         this.fireEvent("datachanged", this);
15664     },
15665
15666     /**
15667      * Query the records by a specified property.
15668      * @param {String} field A field on your records
15669      * @param {String/RegExp} value Either a string that the field
15670      * should start with or a RegExp to test against the field
15671      * @param {Boolean} anyMatch True to match any part not just the beginning
15672      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15673      */
15674     query : function(property, value, anyMatch){
15675         var fn = this.createFilterFn(property, value, anyMatch);
15676         return fn ? this.queryBy(fn) : this.data.clone();
15677     },
15678
15679     /**
15680      * Query by a function. The specified function will be called with each
15681      * record in this data source. If the function returns true the record is included
15682      * in the results.
15683      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15684      * @param {Object} scope (optional) The scope of the function (defaults to this)
15685       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15686      **/
15687     queryBy : function(fn, scope){
15688         var data = this.snapshot || this.data;
15689         return data.filterBy(fn, scope||this);
15690     },
15691
15692     /**
15693      * Collects unique values for a particular dataIndex from this store.
15694      * @param {String} dataIndex The property to collect
15695      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15696      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15697      * @return {Array} An array of the unique values
15698      **/
15699     collect : function(dataIndex, allowNull, bypassFilter){
15700         var d = (bypassFilter === true && this.snapshot) ?
15701                 this.snapshot.items : this.data.items;
15702         var v, sv, r = [], l = {};
15703         for(var i = 0, len = d.length; i < len; i++){
15704             v = d[i].data[dataIndex];
15705             sv = String(v);
15706             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15707                 l[sv] = true;
15708                 r[r.length] = v;
15709             }
15710         }
15711         return r;
15712     },
15713
15714     /**
15715      * Revert to a view of the Record cache with no filtering applied.
15716      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15717      */
15718     clearFilter : function(suppressEvent){
15719         if(this.snapshot && this.snapshot != this.data){
15720             this.data = this.snapshot;
15721             delete this.snapshot;
15722             if(suppressEvent !== true){
15723                 this.fireEvent("datachanged", this);
15724             }
15725         }
15726     },
15727
15728     // private
15729     afterEdit : function(record){
15730         if(this.modified.indexOf(record) == -1){
15731             this.modified.push(record);
15732         }
15733         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15734     },
15735     
15736     // private
15737     afterReject : function(record){
15738         this.modified.remove(record);
15739         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15740     },
15741
15742     // private
15743     afterCommit : function(record){
15744         this.modified.remove(record);
15745         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15746     },
15747
15748     /**
15749      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15750      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15751      */
15752     commitChanges : function(){
15753         var m = this.modified.slice(0);
15754         this.modified = [];
15755         for(var i = 0, len = m.length; i < len; i++){
15756             m[i].commit();
15757         }
15758     },
15759
15760     /**
15761      * Cancel outstanding changes on all changed records.
15762      */
15763     rejectChanges : function(){
15764         var m = this.modified.slice(0);
15765         this.modified = [];
15766         for(var i = 0, len = m.length; i < len; i++){
15767             m[i].reject();
15768         }
15769     },
15770
15771     onMetaChange : function(meta, rtype, o){
15772         this.recordType = rtype;
15773         this.fields = rtype.prototype.fields;
15774         delete this.snapshot;
15775         this.sortInfo = meta.sortInfo || this.sortInfo;
15776         this.modified = [];
15777         this.fireEvent('metachange', this, this.reader.meta);
15778     },
15779     
15780     moveIndex : function(data, type)
15781     {
15782         var index = this.indexOf(data);
15783         
15784         var newIndex = index + type;
15785         
15786         this.remove(data);
15787         
15788         this.insert(newIndex, data);
15789         
15790     }
15791 });/*
15792  * Based on:
15793  * Ext JS Library 1.1.1
15794  * Copyright(c) 2006-2007, Ext JS, LLC.
15795  *
15796  * Originally Released Under LGPL - original licence link has changed is not relivant.
15797  *
15798  * Fork - LGPL
15799  * <script type="text/javascript">
15800  */
15801
15802 /**
15803  * @class Roo.data.SimpleStore
15804  * @extends Roo.data.Store
15805  * Small helper class to make creating Stores from Array data easier.
15806  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15807  * @cfg {Array} fields An array of field definition objects, or field name strings.
15808  * @cfg {Object} an existing reader (eg. copied from another store)
15809  * @cfg {Array} data The multi-dimensional array of data
15810  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15811  * @cfg {Roo.data.Reader} reader  [not-required] 
15812  * @constructor
15813  * @param {Object} config
15814  */
15815 Roo.data.SimpleStore = function(config)
15816 {
15817     Roo.data.SimpleStore.superclass.constructor.call(this, {
15818         isLocal : true,
15819         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15820                 id: config.id
15821             },
15822             Roo.data.Record.create(config.fields)
15823         ),
15824         proxy : new Roo.data.MemoryProxy(config.data)
15825     });
15826     this.load();
15827 };
15828 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15829  * Based on:
15830  * Ext JS Library 1.1.1
15831  * Copyright(c) 2006-2007, Ext JS, LLC.
15832  *
15833  * Originally Released Under LGPL - original licence link has changed is not relivant.
15834  *
15835  * Fork - LGPL
15836  * <script type="text/javascript">
15837  */
15838
15839 /**
15840 /**
15841  * @extends Roo.data.Store
15842  * @class Roo.data.JsonStore
15843  * Small helper class to make creating Stores for JSON data easier. <br/>
15844 <pre><code>
15845 var store = new Roo.data.JsonStore({
15846     url: 'get-images.php',
15847     root: 'images',
15848     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15849 });
15850 </code></pre>
15851  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15852  * JsonReader and HttpProxy (unless inline data is provided).</b>
15853  * @cfg {Array} fields An array of field definition objects, or field name strings.
15854  * @constructor
15855  * @param {Object} config
15856  */
15857 Roo.data.JsonStore = function(c){
15858     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15859         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15860         reader: new Roo.data.JsonReader(c, c.fields)
15861     }));
15862 };
15863 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15864  * Based on:
15865  * Ext JS Library 1.1.1
15866  * Copyright(c) 2006-2007, Ext JS, LLC.
15867  *
15868  * Originally Released Under LGPL - original licence link has changed is not relivant.
15869  *
15870  * Fork - LGPL
15871  * <script type="text/javascript">
15872  */
15873
15874  
15875 Roo.data.Field = function(config){
15876     if(typeof config == "string"){
15877         config = {name: config};
15878     }
15879     Roo.apply(this, config);
15880     
15881     if(!this.type){
15882         this.type = "auto";
15883     }
15884     
15885     var st = Roo.data.SortTypes;
15886     // named sortTypes are supported, here we look them up
15887     if(typeof this.sortType == "string"){
15888         this.sortType = st[this.sortType];
15889     }
15890     
15891     // set default sortType for strings and dates
15892     if(!this.sortType){
15893         switch(this.type){
15894             case "string":
15895                 this.sortType = st.asUCString;
15896                 break;
15897             case "date":
15898                 this.sortType = st.asDate;
15899                 break;
15900             default:
15901                 this.sortType = st.none;
15902         }
15903     }
15904
15905     // define once
15906     var stripRe = /[\$,%]/g;
15907
15908     // prebuilt conversion function for this field, instead of
15909     // switching every time we're reading a value
15910     if(!this.convert){
15911         var cv, dateFormat = this.dateFormat;
15912         switch(this.type){
15913             case "":
15914             case "auto":
15915             case undefined:
15916                 cv = function(v){ return v; };
15917                 break;
15918             case "string":
15919                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15920                 break;
15921             case "int":
15922                 cv = function(v){
15923                     return v !== undefined && v !== null && v !== '' ?
15924                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15925                     };
15926                 break;
15927             case "float":
15928                 cv = function(v){
15929                     return v !== undefined && v !== null && v !== '' ?
15930                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15931                     };
15932                 break;
15933             case "bool":
15934             case "boolean":
15935                 cv = function(v){ return v === true || v === "true" || v == 1; };
15936                 break;
15937             case "date":
15938                 cv = function(v){
15939                     if(!v){
15940                         return '';
15941                     }
15942                     if(v instanceof Date){
15943                         return v;
15944                     }
15945                     if(dateFormat){
15946                         if(dateFormat == "timestamp"){
15947                             return new Date(v*1000);
15948                         }
15949                         return Date.parseDate(v, dateFormat);
15950                     }
15951                     var parsed = Date.parse(v);
15952                     return parsed ? new Date(parsed) : null;
15953                 };
15954              break;
15955             
15956         }
15957         this.convert = cv;
15958     }
15959 };
15960
15961 Roo.data.Field.prototype = {
15962     dateFormat: null,
15963     defaultValue: "",
15964     mapping: null,
15965     sortType : null,
15966     sortDir : "ASC"
15967 };/*
15968  * Based on:
15969  * Ext JS Library 1.1.1
15970  * Copyright(c) 2006-2007, Ext JS, LLC.
15971  *
15972  * Originally Released Under LGPL - original licence link has changed is not relivant.
15973  *
15974  * Fork - LGPL
15975  * <script type="text/javascript">
15976  */
15977  
15978 // Base class for reading structured data from a data source.  This class is intended to be
15979 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15980
15981 /**
15982  * @class Roo.data.DataReader
15983  * @abstract
15984  * Base class for reading structured data from a data source.  This class is intended to be
15985  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15986  */
15987
15988 Roo.data.DataReader = function(meta, recordType){
15989     
15990     this.meta = meta;
15991     
15992     this.recordType = recordType instanceof Array ? 
15993         Roo.data.Record.create(recordType) : recordType;
15994 };
15995
15996 Roo.data.DataReader.prototype = {
15997     
15998     
15999     readerType : 'Data',
16000      /**
16001      * Create an empty record
16002      * @param {Object} data (optional) - overlay some values
16003      * @return {Roo.data.Record} record created.
16004      */
16005     newRow :  function(d) {
16006         var da =  {};
16007         this.recordType.prototype.fields.each(function(c) {
16008             switch( c.type) {
16009                 case 'int' : da[c.name] = 0; break;
16010                 case 'date' : da[c.name] = new Date(); break;
16011                 case 'float' : da[c.name] = 0.0; break;
16012                 case 'boolean' : da[c.name] = false; break;
16013                 default : da[c.name] = ""; break;
16014             }
16015             
16016         });
16017         return new this.recordType(Roo.apply(da, d));
16018     }
16019     
16020     
16021 };/*
16022  * Based on:
16023  * Ext JS Library 1.1.1
16024  * Copyright(c) 2006-2007, Ext JS, LLC.
16025  *
16026  * Originally Released Under LGPL - original licence link has changed is not relivant.
16027  *
16028  * Fork - LGPL
16029  * <script type="text/javascript">
16030  */
16031
16032 /**
16033  * @class Roo.data.DataProxy
16034  * @extends Roo.util.Observable
16035  * @abstract
16036  * This class is an abstract base class for implementations which provide retrieval of
16037  * unformatted data objects.<br>
16038  * <p>
16039  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16040  * (of the appropriate type which knows how to parse the data object) to provide a block of
16041  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16042  * <p>
16043  * Custom implementations must implement the load method as described in
16044  * {@link Roo.data.HttpProxy#load}.
16045  */
16046 Roo.data.DataProxy = function(){
16047     this.addEvents({
16048         /**
16049          * @event beforeload
16050          * Fires before a network request is made to retrieve a data object.
16051          * @param {Object} This DataProxy object.
16052          * @param {Object} params The params parameter to the load function.
16053          */
16054         beforeload : true,
16055         /**
16056          * @event load
16057          * Fires before the load method's callback is called.
16058          * @param {Object} This DataProxy object.
16059          * @param {Object} o The data object.
16060          * @param {Object} arg The callback argument object passed to the load function.
16061          */
16062         load : true,
16063         /**
16064          * @event loadexception
16065          * Fires if an Exception occurs during data retrieval.
16066          * @param {Object} This DataProxy object.
16067          * @param {Object} o The data object.
16068          * @param {Object} arg The callback argument object passed to the load function.
16069          * @param {Object} e The Exception.
16070          */
16071         loadexception : true
16072     });
16073     Roo.data.DataProxy.superclass.constructor.call(this);
16074 };
16075
16076 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16077
16078     /**
16079      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16080      */
16081 /*
16082  * Based on:
16083  * Ext JS Library 1.1.1
16084  * Copyright(c) 2006-2007, Ext JS, LLC.
16085  *
16086  * Originally Released Under LGPL - original licence link has changed is not relivant.
16087  *
16088  * Fork - LGPL
16089  * <script type="text/javascript">
16090  */
16091 /**
16092  * @class Roo.data.MemoryProxy
16093  * @extends Roo.data.DataProxy
16094  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16095  * to the Reader when its load method is called.
16096  * @constructor
16097  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16098  */
16099 Roo.data.MemoryProxy = function(config){
16100     var data = config;
16101     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16102         data = config.data;
16103     }
16104     Roo.data.MemoryProxy.superclass.constructor.call(this);
16105     this.data = data;
16106 };
16107
16108 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16109     
16110     /**
16111      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16112      */
16113     /**
16114      * Load data from the requested source (in this case an in-memory
16115      * data object passed to the constructor), read the data object into
16116      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16117      * process that block using the passed callback.
16118      * @param {Object} params This parameter is not used by the MemoryProxy class.
16119      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16120      * object into a block of Roo.data.Records.
16121      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16122      * The function must be passed <ul>
16123      * <li>The Record block object</li>
16124      * <li>The "arg" argument from the load function</li>
16125      * <li>A boolean success indicator</li>
16126      * </ul>
16127      * @param {Object} scope The scope in which to call the callback
16128      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16129      */
16130     load : function(params, reader, callback, scope, arg){
16131         params = params || {};
16132         var result;
16133         try {
16134             result = reader.readRecords(params.data ? params.data :this.data);
16135         }catch(e){
16136             this.fireEvent("loadexception", this, arg, null, e);
16137             callback.call(scope, null, arg, false);
16138             return;
16139         }
16140         callback.call(scope, result, arg, true);
16141     },
16142     
16143     // private
16144     update : function(params, records){
16145         
16146     }
16147 });/*
16148  * Based on:
16149  * Ext JS Library 1.1.1
16150  * Copyright(c) 2006-2007, Ext JS, LLC.
16151  *
16152  * Originally Released Under LGPL - original licence link has changed is not relivant.
16153  *
16154  * Fork - LGPL
16155  * <script type="text/javascript">
16156  */
16157 /**
16158  * @class Roo.data.HttpProxy
16159  * @extends Roo.data.DataProxy
16160  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16161  * configured to reference a certain URL.<br><br>
16162  * <p>
16163  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16164  * from which the running page was served.<br><br>
16165  * <p>
16166  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16167  * <p>
16168  * Be aware that to enable the browser to parse an XML document, the server must set
16169  * the Content-Type header in the HTTP response to "text/xml".
16170  * @constructor
16171  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16172  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16173  * will be used to make the request.
16174  */
16175 Roo.data.HttpProxy = function(conn){
16176     Roo.data.HttpProxy.superclass.constructor.call(this);
16177     // is conn a conn config or a real conn?
16178     this.conn = conn;
16179     this.useAjax = !conn || !conn.events;
16180   
16181 };
16182
16183 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16184     // thse are take from connection...
16185     
16186     /**
16187      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
16188      */
16189     /**
16190      * @cfg {Object} extraParams  An object containing properties which are used as
16191      * extra parameters to each request made by this object. (defaults to undefined)
16192      */
16193     /**
16194      * @cfg {Object} defaultHeaders   An object containing request headers which are added
16195      *  to each request made by this object. (defaults to undefined)
16196      */
16197     /**
16198      * @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)
16199      */
16200     /**
16201      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16202      */
16203      /**
16204      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16205      * @type Boolean
16206      */
16207   
16208
16209     /**
16210      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16211      * @type Boolean
16212      */
16213     /**
16214      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16215      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16216      * a finer-grained basis than the DataProxy events.
16217      */
16218     getConnection : function(){
16219         return this.useAjax ? Roo.Ajax : this.conn;
16220     },
16221
16222     /**
16223      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16224      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16225      * process that block using the passed callback.
16226      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16227      * for the request to the remote server.
16228      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16229      * object into a block of Roo.data.Records.
16230      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16231      * The function must be passed <ul>
16232      * <li>The Record block object</li>
16233      * <li>The "arg" argument from the load function</li>
16234      * <li>A boolean success indicator</li>
16235      * </ul>
16236      * @param {Object} scope The scope in which to call the callback
16237      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16238      */
16239     load : function(params, reader, callback, scope, arg){
16240         if(this.fireEvent("beforeload", this, params) !== false){
16241             var  o = {
16242                 params : params || {},
16243                 request: {
16244                     callback : callback,
16245                     scope : scope,
16246                     arg : arg
16247                 },
16248                 reader: reader,
16249                 callback : this.loadResponse,
16250                 scope: this
16251             };
16252             if(this.useAjax){
16253                 Roo.applyIf(o, this.conn);
16254                 if(this.activeRequest){
16255                     Roo.Ajax.abort(this.activeRequest);
16256                 }
16257                 this.activeRequest = Roo.Ajax.request(o);
16258             }else{
16259                 this.conn.request(o);
16260             }
16261         }else{
16262             callback.call(scope||this, null, arg, false);
16263         }
16264     },
16265
16266     // private
16267     loadResponse : function(o, success, response){
16268         delete this.activeRequest;
16269         if(!success){
16270             this.fireEvent("loadexception", this, o, response);
16271             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16272             return;
16273         }
16274         var result;
16275         try {
16276             result = o.reader.read(response);
16277         }catch(e){
16278             o.success = false;
16279             o.raw = { errorMsg : response.responseText };
16280             this.fireEvent("loadexception", this, o, response, e);
16281             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16282             return;
16283         }
16284         
16285         this.fireEvent("load", this, o, o.request.arg);
16286         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16287     },
16288
16289     // private
16290     update : function(dataSet){
16291
16292     },
16293
16294     // private
16295     updateResponse : function(dataSet){
16296
16297     }
16298 });/*
16299  * Based on:
16300  * Ext JS Library 1.1.1
16301  * Copyright(c) 2006-2007, Ext JS, LLC.
16302  *
16303  * Originally Released Under LGPL - original licence link has changed is not relivant.
16304  *
16305  * Fork - LGPL
16306  * <script type="text/javascript">
16307  */
16308
16309 /**
16310  * @class Roo.data.ScriptTagProxy
16311  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16312  * other than the originating domain of the running page.<br><br>
16313  * <p>
16314  * <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
16315  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16316  * <p>
16317  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16318  * source code that is used as the source inside a &lt;script> tag.<br><br>
16319  * <p>
16320  * In order for the browser to process the returned data, the server must wrap the data object
16321  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16322  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16323  * depending on whether the callback name was passed:
16324  * <p>
16325  * <pre><code>
16326 boolean scriptTag = false;
16327 String cb = request.getParameter("callback");
16328 if (cb != null) {
16329     scriptTag = true;
16330     response.setContentType("text/javascript");
16331 } else {
16332     response.setContentType("application/x-json");
16333 }
16334 Writer out = response.getWriter();
16335 if (scriptTag) {
16336     out.write(cb + "(");
16337 }
16338 out.print(dataBlock.toJsonString());
16339 if (scriptTag) {
16340     out.write(");");
16341 }
16342 </pre></code>
16343  *
16344  * @constructor
16345  * @param {Object} config A configuration object.
16346  */
16347 Roo.data.ScriptTagProxy = function(config){
16348     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16349     Roo.apply(this, config);
16350     this.head = document.getElementsByTagName("head")[0];
16351 };
16352
16353 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16354
16355 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16356     /**
16357      * @cfg {String} url The URL from which to request the data object.
16358      */
16359     /**
16360      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16361      */
16362     timeout : 30000,
16363     /**
16364      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16365      * the server the name of the callback function set up by the load call to process the returned data object.
16366      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16367      * javascript output which calls this named function passing the data object as its only parameter.
16368      */
16369     callbackParam : "callback",
16370     /**
16371      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16372      * name to the request.
16373      */
16374     nocache : true,
16375
16376     /**
16377      * Load data from the configured URL, read the data object into
16378      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16379      * process that block using the passed callback.
16380      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16381      * for the request to the remote server.
16382      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16383      * object into a block of Roo.data.Records.
16384      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16385      * The function must be passed <ul>
16386      * <li>The Record block object</li>
16387      * <li>The "arg" argument from the load function</li>
16388      * <li>A boolean success indicator</li>
16389      * </ul>
16390      * @param {Object} scope The scope in which to call the callback
16391      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16392      */
16393     load : function(params, reader, callback, scope, arg){
16394         if(this.fireEvent("beforeload", this, params) !== false){
16395
16396             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16397
16398             var url = this.url;
16399             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16400             if(this.nocache){
16401                 url += "&_dc=" + (new Date().getTime());
16402             }
16403             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16404             var trans = {
16405                 id : transId,
16406                 cb : "stcCallback"+transId,
16407                 scriptId : "stcScript"+transId,
16408                 params : params,
16409                 arg : arg,
16410                 url : url,
16411                 callback : callback,
16412                 scope : scope,
16413                 reader : reader
16414             };
16415             var conn = this;
16416
16417             window[trans.cb] = function(o){
16418                 conn.handleResponse(o, trans);
16419             };
16420
16421             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16422
16423             if(this.autoAbort !== false){
16424                 this.abort();
16425             }
16426
16427             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16428
16429             var script = document.createElement("script");
16430             script.setAttribute("src", url);
16431             script.setAttribute("type", "text/javascript");
16432             script.setAttribute("id", trans.scriptId);
16433             this.head.appendChild(script);
16434
16435             this.trans = trans;
16436         }else{
16437             callback.call(scope||this, null, arg, false);
16438         }
16439     },
16440
16441     // private
16442     isLoading : function(){
16443         return this.trans ? true : false;
16444     },
16445
16446     /**
16447      * Abort the current server request.
16448      */
16449     abort : function(){
16450         if(this.isLoading()){
16451             this.destroyTrans(this.trans);
16452         }
16453     },
16454
16455     // private
16456     destroyTrans : function(trans, isLoaded){
16457         this.head.removeChild(document.getElementById(trans.scriptId));
16458         clearTimeout(trans.timeoutId);
16459         if(isLoaded){
16460             window[trans.cb] = undefined;
16461             try{
16462                 delete window[trans.cb];
16463             }catch(e){}
16464         }else{
16465             // if hasn't been loaded, wait for load to remove it to prevent script error
16466             window[trans.cb] = function(){
16467                 window[trans.cb] = undefined;
16468                 try{
16469                     delete window[trans.cb];
16470                 }catch(e){}
16471             };
16472         }
16473     },
16474
16475     // private
16476     handleResponse : function(o, trans){
16477         this.trans = false;
16478         this.destroyTrans(trans, true);
16479         var result;
16480         try {
16481             result = trans.reader.readRecords(o);
16482         }catch(e){
16483             this.fireEvent("loadexception", this, o, trans.arg, e);
16484             trans.callback.call(trans.scope||window, null, trans.arg, false);
16485             return;
16486         }
16487         this.fireEvent("load", this, o, trans.arg);
16488         trans.callback.call(trans.scope||window, result, trans.arg, true);
16489     },
16490
16491     // private
16492     handleFailure : function(trans){
16493         this.trans = false;
16494         this.destroyTrans(trans, false);
16495         this.fireEvent("loadexception", this, null, trans.arg);
16496         trans.callback.call(trans.scope||window, null, trans.arg, false);
16497     }
16498 });/*
16499  * Based on:
16500  * Ext JS Library 1.1.1
16501  * Copyright(c) 2006-2007, Ext JS, LLC.
16502  *
16503  * Originally Released Under LGPL - original licence link has changed is not relivant.
16504  *
16505  * Fork - LGPL
16506  * <script type="text/javascript">
16507  */
16508
16509 /**
16510  * @class Roo.data.JsonReader
16511  * @extends Roo.data.DataReader
16512  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16513  * based on mappings in a provided Roo.data.Record constructor.
16514  * 
16515  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16516  * in the reply previously. 
16517  * 
16518  * <p>
16519  * Example code:
16520  * <pre><code>
16521 var RecordDef = Roo.data.Record.create([
16522     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16523     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16524 ]);
16525 var myReader = new Roo.data.JsonReader({
16526     totalProperty: "results",    // The property which contains the total dataset size (optional)
16527     root: "rows",                // The property which contains an Array of row objects
16528     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16529 }, RecordDef);
16530 </code></pre>
16531  * <p>
16532  * This would consume a JSON file like this:
16533  * <pre><code>
16534 { 'results': 2, 'rows': [
16535     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16536     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16537 }
16538 </code></pre>
16539  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16540  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16541  * paged from the remote server.
16542  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16543  * @cfg {String} root name of the property which contains the Array of row objects.
16544  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16545  * @cfg {Array} fields Array of field definition objects
16546  * @constructor
16547  * Create a new JsonReader
16548  * @param {Object} meta Metadata configuration options
16549  * @param {Object} recordType Either an Array of field definition objects,
16550  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16551  */
16552 Roo.data.JsonReader = function(meta, recordType){
16553     
16554     meta = meta || {};
16555     // set some defaults:
16556     Roo.applyIf(meta, {
16557         totalProperty: 'total',
16558         successProperty : 'success',
16559         root : 'data',
16560         id : 'id'
16561     });
16562     
16563     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16564 };
16565 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16566     
16567     readerType : 'Json',
16568     
16569     /**
16570      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16571      * Used by Store query builder to append _requestMeta to params.
16572      * 
16573      */
16574     metaFromRemote : false,
16575     /**
16576      * This method is only used by a DataProxy which has retrieved data from a remote server.
16577      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16578      * @return {Object} data A data block which is used by an Roo.data.Store object as
16579      * a cache of Roo.data.Records.
16580      */
16581     read : function(response){
16582         var json = response.responseText;
16583        
16584         var o = /* eval:var:o */ eval("("+json+")");
16585         if(!o) {
16586             throw {message: "JsonReader.read: Json object not found"};
16587         }
16588         
16589         if(o.metaData){
16590             
16591             delete this.ef;
16592             this.metaFromRemote = true;
16593             this.meta = o.metaData;
16594             this.recordType = Roo.data.Record.create(o.metaData.fields);
16595             this.onMetaChange(this.meta, this.recordType, o);
16596         }
16597         return this.readRecords(o);
16598     },
16599
16600     // private function a store will implement
16601     onMetaChange : function(meta, recordType, o){
16602
16603     },
16604
16605     /**
16606          * @ignore
16607          */
16608     simpleAccess: function(obj, subsc) {
16609         return obj[subsc];
16610     },
16611
16612         /**
16613          * @ignore
16614          */
16615     getJsonAccessor: function(){
16616         var re = /[\[\.]/;
16617         return function(expr) {
16618             try {
16619                 return(re.test(expr))
16620                     ? new Function("obj", "return obj." + expr)
16621                     : function(obj){
16622                         return obj[expr];
16623                     };
16624             } catch(e){}
16625             return Roo.emptyFn;
16626         };
16627     }(),
16628
16629     /**
16630      * Create a data block containing Roo.data.Records from an XML document.
16631      * @param {Object} o An object which contains an Array of row objects in the property specified
16632      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16633      * which contains the total size of the dataset.
16634      * @return {Object} data A data block which is used by an Roo.data.Store object as
16635      * a cache of Roo.data.Records.
16636      */
16637     readRecords : function(o){
16638         /**
16639          * After any data loads, the raw JSON data is available for further custom processing.
16640          * @type Object
16641          */
16642         this.o = o;
16643         var s = this.meta, Record = this.recordType,
16644             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16645
16646 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16647         if (!this.ef) {
16648             if(s.totalProperty) {
16649                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16650                 }
16651                 if(s.successProperty) {
16652                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16653                 }
16654                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16655                 if (s.id) {
16656                         var g = this.getJsonAccessor(s.id);
16657                         this.getId = function(rec) {
16658                                 var r = g(rec);  
16659                                 return (r === undefined || r === "") ? null : r;
16660                         };
16661                 } else {
16662                         this.getId = function(){return null;};
16663                 }
16664             this.ef = [];
16665             for(var jj = 0; jj < fl; jj++){
16666                 f = fi[jj];
16667                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16668                 this.ef[jj] = this.getJsonAccessor(map);
16669             }
16670         }
16671
16672         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16673         if(s.totalProperty){
16674             var vt = parseInt(this.getTotal(o), 10);
16675             if(!isNaN(vt)){
16676                 totalRecords = vt;
16677             }
16678         }
16679         if(s.successProperty){
16680             var vs = this.getSuccess(o);
16681             if(vs === false || vs === 'false'){
16682                 success = false;
16683             }
16684         }
16685         var records = [];
16686         for(var i = 0; i < c; i++){
16687             var n = root[i];
16688             var values = {};
16689             var id = this.getId(n);
16690             for(var j = 0; j < fl; j++){
16691                 f = fi[j];
16692                                 var v = this.ef[j](n);
16693                                 if (!f.convert) {
16694                                         Roo.log('missing convert for ' + f.name);
16695                                         Roo.log(f);
16696                                         continue;
16697                                 }
16698                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16699             }
16700                         if (!Record) {
16701                                 return {
16702                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16703                                         success : false,
16704                                         records : [],
16705                                         totalRecords : 0
16706                                 };
16707                         }
16708             var record = new Record(values, id);
16709             record.json = n;
16710             records[i] = record;
16711         }
16712         return {
16713             raw : o,
16714             success : success,
16715             records : records,
16716             totalRecords : totalRecords
16717         };
16718     },
16719     // used when loading children.. @see loadDataFromChildren
16720     toLoadData: function(rec)
16721     {
16722         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16723         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16724         return { data : data, total : data.length };
16725         
16726     }
16727 });/*
16728  * Based on:
16729  * Ext JS Library 1.1.1
16730  * Copyright(c) 2006-2007, Ext JS, LLC.
16731  *
16732  * Originally Released Under LGPL - original licence link has changed is not relivant.
16733  *
16734  * Fork - LGPL
16735  * <script type="text/javascript">
16736  */
16737
16738 /**
16739  * @class Roo.data.ArrayReader
16740  * @extends Roo.data.DataReader
16741  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16742  * Each element of that Array represents a row of data fields. The
16743  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16744  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16745  * <p>
16746  * Example code:.
16747  * <pre><code>
16748 var RecordDef = Roo.data.Record.create([
16749     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16750     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16751 ]);
16752 var myReader = new Roo.data.ArrayReader({
16753     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16754 }, RecordDef);
16755 </code></pre>
16756  * <p>
16757  * This would consume an Array like this:
16758  * <pre><code>
16759 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16760   </code></pre>
16761  
16762  * @constructor
16763  * Create a new JsonReader
16764  * @param {Object} meta Metadata configuration options.
16765  * @param {Object|Array} recordType Either an Array of field definition objects
16766  * 
16767  * @cfg {Array} fields Array of field definition objects
16768  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16769  * as specified to {@link Roo.data.Record#create},
16770  * or an {@link Roo.data.Record} object
16771  *
16772  * 
16773  * created using {@link Roo.data.Record#create}.
16774  */
16775 Roo.data.ArrayReader = function(meta, recordType)
16776 {    
16777     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16778 };
16779
16780 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16781     
16782       /**
16783      * Create a data block containing Roo.data.Records from an XML document.
16784      * @param {Object} o An Array of row objects which represents the dataset.
16785      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16786      * a cache of Roo.data.Records.
16787      */
16788     readRecords : function(o)
16789     {
16790         var sid = this.meta ? this.meta.id : null;
16791         var recordType = this.recordType, fields = recordType.prototype.fields;
16792         var records = [];
16793         var root = o;
16794         for(var i = 0; i < root.length; i++){
16795             var n = root[i];
16796             var values = {};
16797             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16798             for(var j = 0, jlen = fields.length; j < jlen; j++){
16799                 var f = fields.items[j];
16800                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16801                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16802                 v = f.convert(v);
16803                 values[f.name] = v;
16804             }
16805             var record = new recordType(values, id);
16806             record.json = n;
16807             records[records.length] = record;
16808         }
16809         return {
16810             records : records,
16811             totalRecords : records.length
16812         };
16813     },
16814     // used when loading children.. @see loadDataFromChildren
16815     toLoadData: function(rec)
16816     {
16817         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16818         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16819         
16820     }
16821     
16822     
16823 });/*
16824  * - LGPL
16825  * * 
16826  */
16827
16828 /**
16829  * @class Roo.bootstrap.form.ComboBox
16830  * @extends Roo.bootstrap.form.TriggerField
16831  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16832  * @cfg {Boolean} append (true|false) default false
16833  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16834  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16835  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16836  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16837  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16838  * @cfg {Boolean} animate default true
16839  * @cfg {Boolean} emptyResultText only for touch device
16840  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16841  * @cfg {String} emptyTitle default ''
16842  * @cfg {Number} width fixed with? experimental
16843  * @constructor
16844  * Create a new ComboBox.
16845  * @param {Object} config Configuration options
16846  */
16847 Roo.bootstrap.form.ComboBox = function(config){
16848     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16849     this.addEvents({
16850         /**
16851          * @event expand
16852          * Fires when the dropdown list is expanded
16853         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16854         */
16855         'expand' : true,
16856         /**
16857          * @event collapse
16858          * Fires when the dropdown list is collapsed
16859         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16860         */
16861         'collapse' : true,
16862         /**
16863          * @event beforeselect
16864          * Fires before a list item is selected. Return false to cancel the selection.
16865         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16866         * @param {Roo.data.Record} record The data record returned from the underlying store
16867         * @param {Number} index The index of the selected item in the dropdown list
16868         */
16869         'beforeselect' : true,
16870         /**
16871          * @event select
16872          * Fires when a list item is selected
16873         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16874         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16875         * @param {Number} index The index of the selected item in the dropdown list
16876         */
16877         'select' : true,
16878         /**
16879          * @event beforequery
16880          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16881          * The event object passed has these properties:
16882         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16883         * @param {String} query The query
16884         * @param {Boolean} forceAll true to force "all" query
16885         * @param {Boolean} cancel true to cancel the query
16886         * @param {Object} e The query event object
16887         */
16888         'beforequery': true,
16889          /**
16890          * @event add
16891          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16892         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16893         */
16894         'add' : true,
16895         /**
16896          * @event edit
16897          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16898         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16899         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16900         */
16901         'edit' : true,
16902         /**
16903          * @event remove
16904          * Fires when the remove value from the combobox array
16905         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16906         */
16907         'remove' : true,
16908         /**
16909          * @event afterremove
16910          * Fires when the remove value from the combobox array
16911         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16912         */
16913         'afterremove' : true,
16914         /**
16915          * @event specialfilter
16916          * Fires when specialfilter
16917             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16918             */
16919         'specialfilter' : true,
16920         /**
16921          * @event tick
16922          * Fires when tick the element
16923             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16924             */
16925         'tick' : true,
16926         /**
16927          * @event touchviewdisplay
16928          * Fires when touch view require special display (default is using displayField)
16929             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16930             * @param {Object} cfg set html .
16931             */
16932         'touchviewdisplay' : true
16933         
16934     });
16935     
16936     this.item = [];
16937     this.tickItems = [];
16938     
16939     this.selectedIndex = -1;
16940     if(this.mode == 'local'){
16941         if(config.queryDelay === undefined){
16942             this.queryDelay = 10;
16943         }
16944         if(config.minChars === undefined){
16945             this.minChars = 0;
16946         }
16947     }
16948 };
16949
16950 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16951      
16952     /**
16953      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16954      * rendering into an Roo.Editor, defaults to false)
16955      */
16956     /**
16957      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16958      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16959      */
16960     /**
16961      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16962      */
16963     /**
16964      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16965      * the dropdown list (defaults to undefined, with no header element)
16966      */
16967
16968      /**
16969      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16970      */
16971      
16972      /**
16973      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16974      */
16975     listWidth: undefined,
16976     /**
16977      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16978      * mode = 'remote' or 'text' if mode = 'local')
16979      */
16980     displayField: undefined,
16981     
16982     /**
16983      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16984      * mode = 'remote' or 'value' if mode = 'local'). 
16985      * Note: use of a valueField requires the user make a selection
16986      * in order for a value to be mapped.
16987      */
16988     valueField: undefined,
16989     /**
16990      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16991      */
16992     modalTitle : '',
16993     
16994     /**
16995      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16996      * field's data value (defaults to the underlying DOM element's name)
16997      */
16998     hiddenName: undefined,
16999     /**
17000      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17001      */
17002     listClass: '',
17003     /**
17004      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17005      */
17006     selectedClass: 'active',
17007     
17008     /**
17009      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17010      */
17011     shadow:'sides',
17012     /**
17013      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17014      * anchor positions (defaults to 'tl-bl')
17015      */
17016     listAlign: 'tl-bl?',
17017     /**
17018      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17019      */
17020     maxHeight: 300,
17021     /**
17022      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17023      * query specified by the allQuery config option (defaults to 'query')
17024      */
17025     triggerAction: 'query',
17026     /**
17027      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17028      * (defaults to 4, does not apply if editable = false)
17029      */
17030     minChars : 4,
17031     /**
17032      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17033      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17034      */
17035     typeAhead: false,
17036     /**
17037      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17038      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17039      */
17040     queryDelay: 500,
17041     /**
17042      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17043      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17044      */
17045     pageSize: 0,
17046     /**
17047      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17048      * when editable = true (defaults to false)
17049      */
17050     selectOnFocus:false,
17051     /**
17052      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17053      */
17054     queryParam: 'query',
17055     /**
17056      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17057      * when mode = 'remote' (defaults to 'Loading...')
17058      */
17059     loadingText: 'Loading...',
17060     /**
17061      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17062      */
17063     resizable: false,
17064     /**
17065      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17066      */
17067     handleHeight : 8,
17068     /**
17069      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17070      * traditional select (defaults to true)
17071      */
17072     editable: true,
17073     /**
17074      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17075      */
17076     allQuery: '',
17077     /**
17078      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17079      */
17080     mode: 'remote',
17081     /**
17082      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17083      * listWidth has a higher value)
17084      */
17085     minListWidth : 70,
17086     /**
17087      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17088      * allow the user to set arbitrary text into the field (defaults to false)
17089      */
17090     forceSelection:false,
17091     /**
17092      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17093      * if typeAhead = true (defaults to 250)
17094      */
17095     typeAheadDelay : 250,
17096     /**
17097      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17098      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17099      */
17100     valueNotFoundText : undefined,
17101     /**
17102      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17103      */
17104     blockFocus : false,
17105     
17106     /**
17107      * @cfg {Boolean} disableClear Disable showing of clear button.
17108      */
17109     disableClear : false,
17110     /**
17111      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17112      */
17113     alwaysQuery : false,
17114     
17115     /**
17116      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17117      */
17118     multiple : false,
17119     
17120     /**
17121      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17122      */
17123     invalidClass : "has-warning",
17124     
17125     /**
17126      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17127      */
17128     validClass : "has-success",
17129     
17130     /**
17131      * @cfg {Boolean} specialFilter (true|false) special filter default false
17132      */
17133     specialFilter : false,
17134     
17135     /**
17136      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17137      */
17138     mobileTouchView : true,
17139     
17140     /**
17141      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17142      */
17143     useNativeIOS : false,
17144     
17145     /**
17146      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17147      */
17148     mobile_restrict_height : false,
17149     
17150     ios_options : false,
17151     
17152     //private
17153     addicon : false,
17154     editicon: false,
17155     
17156     page: 0,
17157     hasQuery: false,
17158     append: false,
17159     loadNext: false,
17160     autoFocus : true,
17161     tickable : false,
17162     btnPosition : 'right',
17163     triggerList : true,
17164     showToggleBtn : true,
17165     animate : true,
17166     emptyResultText: 'Empty',
17167     triggerText : 'Select',
17168     emptyTitle : '',
17169     width : false,
17170     
17171     // element that contains real text value.. (when hidden is used..)
17172     
17173     getAutoCreate : function()
17174     {   
17175         var cfg = false;
17176         //render
17177         /*
17178          * Render classic select for iso
17179          */
17180         
17181         if(Roo.isIOS && this.useNativeIOS){
17182             cfg = this.getAutoCreateNativeIOS();
17183             return cfg;
17184         }
17185         
17186         /*
17187          * Touch Devices
17188          */
17189         
17190         if(Roo.isTouch && this.mobileTouchView){
17191             cfg = this.getAutoCreateTouchView();
17192             return cfg;;
17193         }
17194         
17195         /*
17196          *  Normal ComboBox
17197          */
17198         if(!this.tickable){
17199             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17200             return cfg;
17201         }
17202         
17203         /*
17204          *  ComboBox with tickable selections
17205          */
17206              
17207         var align = this.labelAlign || this.parentLabelAlign();
17208         
17209         cfg = {
17210             cls : 'form-group roo-combobox-tickable' //input-group
17211         };
17212         
17213         var btn_text_select = '';
17214         var btn_text_done = '';
17215         var btn_text_cancel = '';
17216         
17217         if (this.btn_text_show) {
17218             btn_text_select = 'Select';
17219             btn_text_done = 'Done';
17220             btn_text_cancel = 'Cancel'; 
17221         }
17222         
17223         var buttons = {
17224             tag : 'div',
17225             cls : 'tickable-buttons',
17226             cn : [
17227                 {
17228                     tag : 'button',
17229                     type : 'button',
17230                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17231                     //html : this.triggerText
17232                     html: btn_text_select
17233                 },
17234                 {
17235                     tag : 'button',
17236                     type : 'button',
17237                     name : 'ok',
17238                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17239                     //html : 'Done'
17240                     html: btn_text_done
17241                 },
17242                 {
17243                     tag : 'button',
17244                     type : 'button',
17245                     name : 'cancel',
17246                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17247                     //html : 'Cancel'
17248                     html: btn_text_cancel
17249                 }
17250             ]
17251         };
17252         
17253         if(this.editable){
17254             buttons.cn.unshift({
17255                 tag: 'input',
17256                 cls: 'roo-select2-search-field-input'
17257             });
17258         }
17259         
17260         var _this = this;
17261         
17262         Roo.each(buttons.cn, function(c){
17263             if (_this.size) {
17264                 c.cls += ' btn-' + _this.size;
17265             }
17266
17267             if (_this.disabled) {
17268                 c.disabled = true;
17269             }
17270         });
17271         
17272         var box = {
17273             tag: 'div',
17274             style : 'display: contents',
17275             cn: [
17276                 {
17277                     tag: 'input',
17278                     type : 'hidden',
17279                     cls: 'form-hidden-field'
17280                 },
17281                 {
17282                     tag: 'ul',
17283                     cls: 'roo-select2-choices',
17284                     cn:[
17285                         {
17286                             tag: 'li',
17287                             cls: 'roo-select2-search-field',
17288                             cn: [
17289                                 buttons
17290                             ]
17291                         }
17292                     ]
17293                 }
17294             ]
17295         };
17296         
17297         var combobox = {
17298             cls: 'roo-select2-container input-group roo-select2-container-multi',
17299             cn: [
17300                 
17301                 box
17302 //                {
17303 //                    tag: 'ul',
17304 //                    cls: 'typeahead typeahead-long dropdown-menu',
17305 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17306 //                }
17307             ]
17308         };
17309         
17310         if(this.hasFeedback && !this.allowBlank){
17311             
17312             var feedback = {
17313                 tag: 'span',
17314                 cls: 'glyphicon form-control-feedback'
17315             };
17316
17317             combobox.cn.push(feedback);
17318         }
17319         
17320         
17321         
17322         var indicator = {
17323             tag : 'i',
17324             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17325             tooltip : 'This field is required'
17326         };
17327          
17328         if (this.allowBlank) {
17329             indicator = {
17330                 tag : 'i',
17331                 style : 'display:none'
17332             };
17333         } 
17334         if (align ==='left' && this.fieldLabel.length) {
17335             
17336             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17337             
17338             cfg.cn = [
17339                 indicator,
17340                 {
17341                     tag: 'label',
17342                     'for' :  id,
17343                     cls : 'control-label col-form-label',
17344                     html : this.fieldLabel
17345
17346                 },
17347                 {
17348                     cls : "", 
17349                     cn: [
17350                         combobox
17351                     ]
17352                 }
17353
17354             ];
17355             
17356             var labelCfg = cfg.cn[1];
17357             var contentCfg = cfg.cn[2];
17358             
17359
17360             if(this.indicatorpos == 'right'){
17361                 
17362                 cfg.cn = [
17363                     {
17364                         tag: 'label',
17365                         'for' :  id,
17366                         cls : 'control-label col-form-label',
17367                         cn : [
17368                             {
17369                                 tag : 'span',
17370                                 html : this.fieldLabel
17371                             },
17372                             indicator
17373                         ]
17374                     },
17375                     {
17376                         cls : "",
17377                         cn: [
17378                             combobox
17379                         ]
17380                     }
17381
17382                 ];
17383                 
17384                 
17385                 
17386                 labelCfg = cfg.cn[0];
17387                 contentCfg = cfg.cn[1];
17388             
17389             }
17390             
17391             if(this.labelWidth > 12){
17392                 labelCfg.style = "width: " + this.labelWidth + 'px';
17393             }
17394             if(this.width * 1 > 0){
17395                 contentCfg.style = "width: " + this.width + 'px';
17396             }
17397             if(this.labelWidth < 13 && this.labelmd == 0){
17398                 this.labelmd = this.labelWidth;
17399             }
17400             
17401             if(this.labellg > 0){
17402                 labelCfg.cls += ' col-lg-' + this.labellg;
17403                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17404             }
17405             
17406             if(this.labelmd > 0){
17407                 labelCfg.cls += ' col-md-' + this.labelmd;
17408                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17409             }
17410             
17411             if(this.labelsm > 0){
17412                 labelCfg.cls += ' col-sm-' + this.labelsm;
17413                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17414             }
17415             
17416             if(this.labelxs > 0){
17417                 labelCfg.cls += ' col-xs-' + this.labelxs;
17418                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17419             }
17420                 
17421                 
17422         } else if ( this.fieldLabel.length) {
17423 //                Roo.log(" label");
17424                  cfg.cn = [
17425                    indicator,
17426                     {
17427                         tag: 'label',
17428                         //cls : 'input-group-addon',
17429                         html : this.fieldLabel
17430                     },
17431                     combobox
17432                 ];
17433                 
17434                 if(this.indicatorpos == 'right'){
17435                     cfg.cn = [
17436                         {
17437                             tag: 'label',
17438                             //cls : 'input-group-addon',
17439                             html : this.fieldLabel
17440                         },
17441                         indicator,
17442                         combobox
17443                     ];
17444                     
17445                 }
17446
17447         } else {
17448             
17449 //                Roo.log(" no label && no align");
17450                 cfg = combobox
17451                      
17452                 
17453         }
17454          
17455         var settings=this;
17456         ['xs','sm','md','lg'].map(function(size){
17457             if (settings[size]) {
17458                 cfg.cls += ' col-' + size + '-' + settings[size];
17459             }
17460         });
17461         
17462         return cfg;
17463         
17464     },
17465     
17466     _initEventsCalled : false,
17467     
17468     // private
17469     initEvents: function()
17470     {   
17471         if (this._initEventsCalled) { // as we call render... prevent looping...
17472             return;
17473         }
17474         this._initEventsCalled = true;
17475         
17476         if (!this.store) {
17477             throw "can not find store for combo";
17478         }
17479         
17480         this.indicator = this.indicatorEl();
17481         
17482         this.store = Roo.factory(this.store, Roo.data);
17483         this.store.parent = this;
17484         
17485         // if we are building from html. then this element is so complex, that we can not really
17486         // use the rendered HTML.
17487         // so we have to trash and replace the previous code.
17488         if (Roo.XComponent.build_from_html) {
17489             // remove this element....
17490             var e = this.el.dom, k=0;
17491             while (e ) { e = e.previousSibling;  ++k;}
17492
17493             this.el.remove();
17494             
17495             this.el=false;
17496             this.rendered = false;
17497             
17498             this.render(this.parent().getChildContainer(true), k);
17499         }
17500         
17501         if(Roo.isIOS && this.useNativeIOS){
17502             this.initIOSView();
17503             return;
17504         }
17505         
17506         /*
17507          * Touch Devices
17508          */
17509         
17510         if(Roo.isTouch && this.mobileTouchView){
17511             this.initTouchView();
17512             return;
17513         }
17514         
17515         if(this.tickable){
17516             this.initTickableEvents();
17517             return;
17518         }
17519         
17520         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17521         
17522         if(this.hiddenName){
17523             
17524             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17525             
17526             this.hiddenField.dom.value =
17527                 this.hiddenValue !== undefined ? this.hiddenValue :
17528                 this.value !== undefined ? this.value : '';
17529
17530             // prevent input submission
17531             this.el.dom.removeAttribute('name');
17532             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17533              
17534              
17535         }
17536         //if(Roo.isGecko){
17537         //    this.el.dom.setAttribute('autocomplete', 'off');
17538         //}
17539         
17540         var cls = 'x-combo-list';
17541         
17542         //this.list = new Roo.Layer({
17543         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17544         //});
17545         
17546         var _this = this;
17547         
17548         (function(){
17549             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17550             _this.list.setWidth(lw);
17551         }).defer(100);
17552         
17553         this.list.on('mouseover', this.onViewOver, this);
17554         this.list.on('mousemove', this.onViewMove, this);
17555         this.list.on('scroll', this.onViewScroll, this);
17556         
17557         /*
17558         this.list.swallowEvent('mousewheel');
17559         this.assetHeight = 0;
17560
17561         if(this.title){
17562             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17563             this.assetHeight += this.header.getHeight();
17564         }
17565
17566         this.innerList = this.list.createChild({cls:cls+'-inner'});
17567         this.innerList.on('mouseover', this.onViewOver, this);
17568         this.innerList.on('mousemove', this.onViewMove, this);
17569         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17570         
17571         if(this.allowBlank && !this.pageSize && !this.disableClear){
17572             this.footer = this.list.createChild({cls:cls+'-ft'});
17573             this.pageTb = new Roo.Toolbar(this.footer);
17574            
17575         }
17576         if(this.pageSize){
17577             this.footer = this.list.createChild({cls:cls+'-ft'});
17578             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17579                     {pageSize: this.pageSize});
17580             
17581         }
17582         
17583         if (this.pageTb && this.allowBlank && !this.disableClear) {
17584             var _this = this;
17585             this.pageTb.add(new Roo.Toolbar.Fill(), {
17586                 cls: 'x-btn-icon x-btn-clear',
17587                 text: '&#160;',
17588                 handler: function()
17589                 {
17590                     _this.collapse();
17591                     _this.clearValue();
17592                     _this.onSelect(false, -1);
17593                 }
17594             });
17595         }
17596         if (this.footer) {
17597             this.assetHeight += this.footer.getHeight();
17598         }
17599         */
17600             
17601         if(!this.tpl){
17602             this.tpl = Roo.bootstrap.version == 4 ?
17603                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17604                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17605         }
17606
17607         this.view = new Roo.View(this.list, this.tpl, {
17608             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17609         });
17610         //this.view.wrapEl.setDisplayed(false);
17611         this.view.on('click', this.onViewClick, this);
17612         
17613         
17614         this.store.on('beforeload', this.onBeforeLoad, this);
17615         this.store.on('load', this.onLoad, this);
17616         this.store.on('loadexception', this.onLoadException, this);
17617         /*
17618         if(this.resizable){
17619             this.resizer = new Roo.Resizable(this.list,  {
17620                pinned:true, handles:'se'
17621             });
17622             this.resizer.on('resize', function(r, w, h){
17623                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17624                 this.listWidth = w;
17625                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17626                 this.restrictHeight();
17627             }, this);
17628             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17629         }
17630         */
17631         if(!this.editable){
17632             this.editable = true;
17633             this.setEditable(false);
17634         }
17635         
17636         /*
17637         
17638         if (typeof(this.events.add.listeners) != 'undefined') {
17639             
17640             this.addicon = this.wrap.createChild(
17641                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17642        
17643             this.addicon.on('click', function(e) {
17644                 this.fireEvent('add', this);
17645             }, this);
17646         }
17647         if (typeof(this.events.edit.listeners) != 'undefined') {
17648             
17649             this.editicon = this.wrap.createChild(
17650                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17651             if (this.addicon) {
17652                 this.editicon.setStyle('margin-left', '40px');
17653             }
17654             this.editicon.on('click', function(e) {
17655                 
17656                 // we fire even  if inothing is selected..
17657                 this.fireEvent('edit', this, this.lastData );
17658                 
17659             }, this);
17660         }
17661         */
17662         
17663         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17664             "up" : function(e){
17665                 this.inKeyMode = true;
17666                 this.selectPrev();
17667             },
17668
17669             "down" : function(e){
17670                 if(!this.isExpanded()){
17671                     this.onTriggerClick();
17672                 }else{
17673                     this.inKeyMode = true;
17674                     this.selectNext();
17675                 }
17676             },
17677
17678             "enter" : function(e){
17679 //                this.onViewClick();
17680                 //return true;
17681                 this.collapse();
17682                 
17683                 if(this.fireEvent("specialkey", this, e)){
17684                     this.onViewClick(false);
17685                 }
17686                 
17687                 return true;
17688             },
17689
17690             "esc" : function(e){
17691                 this.collapse();
17692             },
17693
17694             "tab" : function(e){
17695                 this.collapse();
17696                 
17697                 if(this.fireEvent("specialkey", this, e)){
17698                     this.onViewClick(false);
17699                 }
17700                 
17701                 return true;
17702             },
17703
17704             scope : this,
17705
17706             doRelay : function(foo, bar, hname){
17707                 if(hname == 'down' || this.scope.isExpanded()){
17708                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17709                 }
17710                 return true;
17711             },
17712
17713             forceKeyDown: true
17714         });
17715         
17716         
17717         this.queryDelay = Math.max(this.queryDelay || 10,
17718                 this.mode == 'local' ? 10 : 250);
17719         
17720         
17721         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17722         
17723         if(this.typeAhead){
17724             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17725         }
17726         if(this.editable !== false){
17727             this.inputEl().on("keyup", this.onKeyUp, this);
17728         }
17729         if(this.forceSelection){
17730             this.inputEl().on('blur', this.doForce, this);
17731         }
17732         
17733         if(this.multiple){
17734             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17735             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17736         }
17737     },
17738     
17739     initTickableEvents: function()
17740     {   
17741         this.createList();
17742         
17743         if(this.hiddenName){
17744             
17745             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17746             
17747             this.hiddenField.dom.value =
17748                 this.hiddenValue !== undefined ? this.hiddenValue :
17749                 this.value !== undefined ? this.value : '';
17750
17751             // prevent input submission
17752             this.el.dom.removeAttribute('name');
17753             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17754              
17755              
17756         }
17757         
17758 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17759         
17760         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17761         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17762         if(this.triggerList){
17763             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17764         }
17765          
17766         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17767         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17768         
17769         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17770         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17771         
17772         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17773         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17774         
17775         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17776         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17777         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17778         
17779         this.okBtn.hide();
17780         this.cancelBtn.hide();
17781         
17782         var _this = this;
17783         
17784         (function(){
17785             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17786             _this.list.setWidth(lw);
17787         }).defer(100);
17788         
17789         this.list.on('mouseover', this.onViewOver, this);
17790         this.list.on('mousemove', this.onViewMove, this);
17791         
17792         this.list.on('scroll', this.onViewScroll, this);
17793         
17794         if(!this.tpl){
17795             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17796                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17797         }
17798
17799         this.view = new Roo.View(this.list, this.tpl, {
17800             singleSelect:true,
17801             tickable:true,
17802             parent:this,
17803             store: this.store,
17804             selectedClass: this.selectedClass
17805         });
17806         
17807         //this.view.wrapEl.setDisplayed(false);
17808         this.view.on('click', this.onViewClick, this);
17809         
17810         
17811         
17812         this.store.on('beforeload', this.onBeforeLoad, this);
17813         this.store.on('load', this.onLoad, this);
17814         this.store.on('loadexception', this.onLoadException, this);
17815         
17816         if(this.editable){
17817             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17818                 "up" : function(e){
17819                     this.inKeyMode = true;
17820                     this.selectPrev();
17821                 },
17822
17823                 "down" : function(e){
17824                     this.inKeyMode = true;
17825                     this.selectNext();
17826                 },
17827
17828                 "enter" : function(e){
17829                     if(this.fireEvent("specialkey", this, e)){
17830                         this.onViewClick(false);
17831                     }
17832                     
17833                     return true;
17834                 },
17835
17836                 "esc" : function(e){
17837                     this.onTickableFooterButtonClick(e, false, false);
17838                 },
17839
17840                 "tab" : function(e){
17841                     this.fireEvent("specialkey", this, e);
17842                     
17843                     this.onTickableFooterButtonClick(e, false, false);
17844                     
17845                     return true;
17846                 },
17847
17848                 scope : this,
17849
17850                 doRelay : function(e, fn, key){
17851                     if(this.scope.isExpanded()){
17852                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17853                     }
17854                     return true;
17855                 },
17856
17857                 forceKeyDown: true
17858             });
17859         }
17860         
17861         this.queryDelay = Math.max(this.queryDelay || 10,
17862                 this.mode == 'local' ? 10 : 250);
17863         
17864         
17865         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17866         
17867         if(this.typeAhead){
17868             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17869         }
17870         
17871         if(this.editable !== false){
17872             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17873         }
17874         
17875         this.indicator = this.indicatorEl();
17876         
17877         if(this.indicator){
17878             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17879             this.indicator.hide();
17880         }
17881         
17882     },
17883
17884     onDestroy : function(){
17885         if(this.view){
17886             this.view.setStore(null);
17887             this.view.el.removeAllListeners();
17888             this.view.el.remove();
17889             this.view.purgeListeners();
17890         }
17891         if(this.list){
17892             this.list.dom.innerHTML  = '';
17893         }
17894         
17895         if(this.store){
17896             this.store.un('beforeload', this.onBeforeLoad, this);
17897             this.store.un('load', this.onLoad, this);
17898             this.store.un('loadexception', this.onLoadException, this);
17899         }
17900         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17901     },
17902
17903     // private
17904     fireKey : function(e){
17905         if(e.isNavKeyPress() && !this.list.isVisible()){
17906             this.fireEvent("specialkey", this, e);
17907         }
17908     },
17909
17910     // private
17911     onResize: function(w, h)
17912     {
17913         
17914         
17915 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17916 //        
17917 //        if(typeof w != 'number'){
17918 //            // we do not handle it!?!?
17919 //            return;
17920 //        }
17921 //        var tw = this.trigger.getWidth();
17922 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17923 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17924 //        var x = w - tw;
17925 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17926 //            
17927 //        //this.trigger.setStyle('left', x+'px');
17928 //        
17929 //        if(this.list && this.listWidth === undefined){
17930 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17931 //            this.list.setWidth(lw);
17932 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17933 //        }
17934         
17935     
17936         
17937     },
17938
17939     /**
17940      * Allow or prevent the user from directly editing the field text.  If false is passed,
17941      * the user will only be able to select from the items defined in the dropdown list.  This method
17942      * is the runtime equivalent of setting the 'editable' config option at config time.
17943      * @param {Boolean} value True to allow the user to directly edit the field text
17944      */
17945     setEditable : function(value){
17946         if(value == this.editable){
17947             return;
17948         }
17949         this.editable = value;
17950         if(!value){
17951             this.inputEl().dom.setAttribute('readOnly', true);
17952             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17953             this.inputEl().addClass('x-combo-noedit');
17954         }else{
17955             this.inputEl().dom.removeAttribute('readOnly');
17956             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17957             this.inputEl().removeClass('x-combo-noedit');
17958         }
17959     },
17960
17961     // private
17962     
17963     onBeforeLoad : function(combo,opts){
17964         if(!this.hasFocus){
17965             return;
17966         }
17967          if (!opts.add) {
17968             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17969          }
17970         this.restrictHeight();
17971         this.selectedIndex = -1;
17972     },
17973
17974     // private
17975     onLoad : function(){
17976         
17977         this.hasQuery = false;
17978         
17979         if(!this.hasFocus){
17980             return;
17981         }
17982         
17983         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17984             this.loading.hide();
17985         }
17986         
17987         if(this.store.getCount() > 0){
17988             
17989             this.expand();
17990             this.restrictHeight();
17991             if(this.lastQuery == this.allQuery){
17992                 if(this.editable && !this.tickable){
17993                     this.inputEl().dom.select();
17994                 }
17995                 
17996                 if(
17997                     !this.selectByValue(this.value, true) &&
17998                     this.autoFocus && 
17999                     (
18000                         !this.store.lastOptions ||
18001                         typeof(this.store.lastOptions.add) == 'undefined' || 
18002                         this.store.lastOptions.add != true
18003                     )
18004                 ){
18005                     this.select(0, true);
18006                 }
18007             }else{
18008                 if(this.autoFocus){
18009                     this.selectNext();
18010                 }
18011                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18012                     this.taTask.delay(this.typeAheadDelay);
18013                 }
18014             }
18015         }else{
18016             this.onEmptyResults();
18017         }
18018         
18019         //this.el.focus();
18020     },
18021     // private
18022     onLoadException : function()
18023     {
18024         this.hasQuery = false;
18025         
18026         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18027             this.loading.hide();
18028         }
18029         
18030         if(this.tickable && this.editable){
18031             return;
18032         }
18033         
18034         this.collapse();
18035         // only causes errors at present
18036         //Roo.log(this.store.reader.jsonData);
18037         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18038             // fixme
18039             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18040         //}
18041         
18042         
18043     },
18044     // private
18045     onTypeAhead : function(){
18046         if(this.store.getCount() > 0){
18047             var r = this.store.getAt(0);
18048             var newValue = r.data[this.displayField];
18049             var len = newValue.length;
18050             var selStart = this.getRawValue().length;
18051             
18052             if(selStart != len){
18053                 this.setRawValue(newValue);
18054                 this.selectText(selStart, newValue.length);
18055             }
18056         }
18057     },
18058
18059     // private
18060     onSelect : function(record, index){
18061         
18062         if(this.fireEvent('beforeselect', this, record, index) !== false){
18063         
18064             this.setFromData(index > -1 ? record.data : false);
18065             
18066             this.collapse();
18067             this.fireEvent('select', this, record, index);
18068         }
18069     },
18070
18071     /**
18072      * Returns the currently selected field value or empty string if no value is set.
18073      * @return {String} value The selected value
18074      */
18075     getValue : function()
18076     {
18077         if(Roo.isIOS && this.useNativeIOS){
18078             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18079         }
18080         
18081         if(this.multiple){
18082             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18083         }
18084         
18085         if(this.valueField){
18086             return typeof this.value != 'undefined' ? this.value : '';
18087         }else{
18088             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18089         }
18090     },
18091     
18092     getRawValue : function()
18093     {
18094         if(Roo.isIOS && this.useNativeIOS){
18095             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18096         }
18097         
18098         var v = this.inputEl().getValue();
18099         
18100         return v;
18101     },
18102
18103     /**
18104      * Clears any text/value currently set in the field
18105      */
18106     clearValue : function(){
18107         
18108         if(this.hiddenField){
18109             this.hiddenField.dom.value = '';
18110         }
18111         this.value = '';
18112         this.setRawValue('');
18113         this.lastSelectionText = '';
18114         this.lastData = false;
18115         
18116         var close = this.closeTriggerEl();
18117         
18118         if(close){
18119             close.hide();
18120         }
18121         
18122         this.validate();
18123         
18124     },
18125
18126     /**
18127      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18128      * will be displayed in the field.  If the value does not match the data value of an existing item,
18129      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18130      * Otherwise the field will be blank (although the value will still be set).
18131      * @param {String} value The value to match
18132      */
18133     setValue : function(v)
18134     {
18135         if(Roo.isIOS && this.useNativeIOS){
18136             this.setIOSValue(v);
18137             return;
18138         }
18139         
18140         if(this.multiple){
18141             this.syncValue();
18142             return;
18143         }
18144         
18145         var text = v;
18146         if(this.valueField){
18147             var r = this.findRecord(this.valueField, v);
18148             if(r){
18149                 text = r.data[this.displayField];
18150             }else if(this.valueNotFoundText !== undefined){
18151                 text = this.valueNotFoundText;
18152             }
18153         }
18154         this.lastSelectionText = text;
18155         if(this.hiddenField){
18156             this.hiddenField.dom.value = v;
18157         }
18158         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18159         this.value = v;
18160         
18161         var close = this.closeTriggerEl();
18162         
18163         if(close){
18164             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18165         }
18166         
18167         this.validate();
18168     },
18169     /**
18170      * @property {Object} the last set data for the element
18171      */
18172     
18173     lastData : false,
18174     /**
18175      * Sets the value of the field based on a object which is related to the record format for the store.
18176      * @param {Object} value the value to set as. or false on reset?
18177      */
18178     setFromData : function(o){
18179         
18180         if(this.multiple){
18181             this.addItem(o);
18182             return;
18183         }
18184             
18185         var dv = ''; // display value
18186         var vv = ''; // value value..
18187         this.lastData = o;
18188         if (this.displayField) {
18189             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18190         } else {
18191             // this is an error condition!!!
18192             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18193         }
18194         
18195         if(this.valueField){
18196             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18197         }
18198         
18199         var close = this.closeTriggerEl();
18200         
18201         if(close){
18202             if(dv.length || vv * 1 > 0){
18203                 close.show() ;
18204                 this.blockFocus=true;
18205             } else {
18206                 close.hide();
18207             }             
18208         }
18209         
18210         if(this.hiddenField){
18211             this.hiddenField.dom.value = vv;
18212             
18213             this.lastSelectionText = dv;
18214             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18215             this.value = vv;
18216             return;
18217         }
18218         // no hidden field.. - we store the value in 'value', but still display
18219         // display field!!!!
18220         this.lastSelectionText = dv;
18221         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18222         this.value = vv;
18223         
18224         
18225         
18226     },
18227     // private
18228     reset : function(){
18229         // overridden so that last data is reset..
18230         
18231         if(this.multiple){
18232             this.clearItem();
18233             return;
18234         }
18235         
18236         this.setValue(this.originalValue);
18237         //this.clearInvalid();
18238         this.lastData = false;
18239         if (this.view) {
18240             this.view.clearSelections();
18241         }
18242         
18243         this.validate();
18244     },
18245     // private
18246     findRecord : function(prop, value){
18247         var record;
18248         if(this.store.getCount() > 0){
18249             this.store.each(function(r){
18250                 if(r.data[prop] == value){
18251                     record = r;
18252                     return false;
18253                 }
18254                 return true;
18255             });
18256         }
18257         return record;
18258     },
18259     
18260     getName: function()
18261     {
18262         // returns hidden if it's set..
18263         if (!this.rendered) {return ''};
18264         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18265         
18266     },
18267     // private
18268     onViewMove : function(e, t){
18269         this.inKeyMode = false;
18270     },
18271
18272     // private
18273     onViewOver : function(e, t){
18274         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18275             return;
18276         }
18277         var item = this.view.findItemFromChild(t);
18278         
18279         if(item){
18280             var index = this.view.indexOf(item);
18281             this.select(index, false);
18282         }
18283     },
18284
18285     // private
18286     onViewClick : function(view, doFocus, el, e)
18287     {
18288         var index = this.view.getSelectedIndexes()[0];
18289         
18290         var r = this.store.getAt(index);
18291         
18292         if(this.tickable){
18293             
18294             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18295                 return;
18296             }
18297             
18298             var rm = false;
18299             var _this = this;
18300             
18301             Roo.each(this.tickItems, function(v,k){
18302                 
18303                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18304                     Roo.log(v);
18305                     _this.tickItems.splice(k, 1);
18306                     
18307                     if(typeof(e) == 'undefined' && view == false){
18308                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18309                     }
18310                     
18311                     rm = true;
18312                     return;
18313                 }
18314             });
18315             
18316             if(rm){
18317                 return;
18318             }
18319             
18320             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18321                 this.tickItems.push(r.data);
18322             }
18323             
18324             if(typeof(e) == 'undefined' && view == false){
18325                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18326             }
18327                     
18328             return;
18329         }
18330         
18331         if(r){
18332             this.onSelect(r, index);
18333         }
18334         if(doFocus !== false && !this.blockFocus){
18335             this.inputEl().focus();
18336         }
18337     },
18338
18339     // private
18340     restrictHeight : function(){
18341         //this.innerList.dom.style.height = '';
18342         //var inner = this.innerList.dom;
18343         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18344         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18345         //this.list.beginUpdate();
18346         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18347         this.list.alignTo(this.inputEl(), this.listAlign);
18348         this.list.alignTo(this.inputEl(), this.listAlign);
18349         //this.list.endUpdate();
18350     },
18351
18352     // private
18353     onEmptyResults : function(){
18354         
18355         if(this.tickable && this.editable){
18356             this.hasFocus = false;
18357             this.restrictHeight();
18358             return;
18359         }
18360         
18361         this.collapse();
18362     },
18363
18364     /**
18365      * Returns true if the dropdown list is expanded, else false.
18366      */
18367     isExpanded : function(){
18368         return this.list.isVisible();
18369     },
18370
18371     /**
18372      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18373      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18374      * @param {String} value The data value of the item to select
18375      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18376      * selected item if it is not currently in view (defaults to true)
18377      * @return {Boolean} True if the value matched an item in the list, else false
18378      */
18379     selectByValue : function(v, scrollIntoView){
18380         if(v !== undefined && v !== null){
18381             var r = this.findRecord(this.valueField || this.displayField, v);
18382             if(r){
18383                 this.select(this.store.indexOf(r), scrollIntoView);
18384                 return true;
18385             }
18386         }
18387         return false;
18388     },
18389
18390     /**
18391      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18392      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18393      * @param {Number} index The zero-based index of the list item to select
18394      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18395      * selected item if it is not currently in view (defaults to true)
18396      */
18397     select : function(index, scrollIntoView){
18398         this.selectedIndex = index;
18399         this.view.select(index);
18400         if(scrollIntoView !== false){
18401             var el = this.view.getNode(index);
18402             /*
18403              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18404              */
18405             if(el){
18406                 this.list.scrollChildIntoView(el, false);
18407             }
18408         }
18409     },
18410
18411     // private
18412     selectNext : function(){
18413         var ct = this.store.getCount();
18414         if(ct > 0){
18415             if(this.selectedIndex == -1){
18416                 this.select(0);
18417             }else if(this.selectedIndex < ct-1){
18418                 this.select(this.selectedIndex+1);
18419             }
18420         }
18421     },
18422
18423     // private
18424     selectPrev : function(){
18425         var ct = this.store.getCount();
18426         if(ct > 0){
18427             if(this.selectedIndex == -1){
18428                 this.select(0);
18429             }else if(this.selectedIndex != 0){
18430                 this.select(this.selectedIndex-1);
18431             }
18432         }
18433     },
18434
18435     // private
18436     onKeyUp : function(e){
18437         if(this.editable !== false && !e.isSpecialKey()){
18438             this.lastKey = e.getKey();
18439             this.dqTask.delay(this.queryDelay);
18440         }
18441     },
18442
18443     // private
18444     validateBlur : function(){
18445         return !this.list || !this.list.isVisible();   
18446     },
18447
18448     // private
18449     initQuery : function(){
18450         
18451         var v = this.getRawValue();
18452         
18453         if(this.tickable && this.editable){
18454             v = this.tickableInputEl().getValue();
18455         }
18456         
18457         this.doQuery(v);
18458     },
18459
18460     // private
18461     doForce : function(){
18462         if(this.inputEl().dom.value.length > 0){
18463             this.inputEl().dom.value =
18464                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18465              
18466         }
18467     },
18468
18469     /**
18470      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18471      * query allowing the query action to be canceled if needed.
18472      * @param {String} query The SQL query to execute
18473      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18474      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18475      * saved in the current store (defaults to false)
18476      */
18477     doQuery : function(q, forceAll){
18478         
18479         if(q === undefined || q === null){
18480             q = '';
18481         }
18482         var qe = {
18483             query: q,
18484             forceAll: forceAll,
18485             combo: this,
18486             cancel:false
18487         };
18488         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18489             return false;
18490         }
18491         q = qe.query;
18492         
18493         forceAll = qe.forceAll;
18494         if(forceAll === true || (q.length >= this.minChars)){
18495             
18496             this.hasQuery = true;
18497             
18498             if(this.lastQuery != q || this.alwaysQuery){
18499                 this.lastQuery = q;
18500                 if(this.mode == 'local'){
18501                     this.selectedIndex = -1;
18502                     if(forceAll){
18503                         this.store.clearFilter();
18504                     }else{
18505                         
18506                         if(this.specialFilter){
18507                             this.fireEvent('specialfilter', this);
18508                             this.onLoad();
18509                             return;
18510                         }
18511                         
18512                         this.store.filter(this.displayField, q);
18513                     }
18514                     
18515                     this.store.fireEvent("datachanged", this.store);
18516                     
18517                     this.onLoad();
18518                     
18519                     
18520                 }else{
18521                     
18522                     this.store.baseParams[this.queryParam] = q;
18523                     
18524                     var options = {params : this.getParams(q)};
18525                     
18526                     if(this.loadNext){
18527                         options.add = true;
18528                         options.params.start = this.page * this.pageSize;
18529                     }
18530                     
18531                     this.store.load(options);
18532                     
18533                     /*
18534                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18535                      *  we should expand the list on onLoad
18536                      *  so command out it
18537                      */
18538 //                    this.expand();
18539                 }
18540             }else{
18541                 this.selectedIndex = -1;
18542                 this.onLoad();   
18543             }
18544         }
18545         
18546         this.loadNext = false;
18547     },
18548     
18549     // private
18550     getParams : function(q){
18551         var p = {};
18552         //p[this.queryParam] = q;
18553         
18554         if(this.pageSize){
18555             p.start = 0;
18556             p.limit = this.pageSize;
18557         }
18558         return p;
18559     },
18560
18561     /**
18562      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18563      */
18564     collapse : function(){
18565         if(!this.isExpanded()){
18566             return;
18567         }
18568         
18569         this.list.hide();
18570         
18571         this.hasFocus = false;
18572         
18573         if(this.tickable){
18574             this.okBtn.hide();
18575             this.cancelBtn.hide();
18576             this.trigger.show();
18577             
18578             if(this.editable){
18579                 this.tickableInputEl().dom.value = '';
18580                 this.tickableInputEl().blur();
18581             }
18582             
18583         }
18584         
18585         Roo.get(document).un('mousedown', this.collapseIf, this);
18586         Roo.get(document).un('mousewheel', this.collapseIf, this);
18587         if (!this.editable) {
18588             Roo.get(document).un('keydown', this.listKeyPress, this);
18589         }
18590         this.fireEvent('collapse', this);
18591         
18592         this.validate();
18593     },
18594
18595     // private
18596     collapseIf : function(e){
18597         var in_combo  = e.within(this.el);
18598         var in_list =  e.within(this.list);
18599         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18600         
18601         if (in_combo || in_list || is_list) {
18602             //e.stopPropagation();
18603             return;
18604         }
18605         
18606         if(this.tickable){
18607             this.onTickableFooterButtonClick(e, false, false);
18608         }
18609
18610         this.collapse();
18611         
18612     },
18613
18614     /**
18615      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18616      */
18617     expand : function(){
18618        
18619         if(this.isExpanded() || !this.hasFocus){
18620             return;
18621         }
18622         
18623         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18624         this.list.setWidth(lw);
18625         
18626         Roo.log('expand');
18627         
18628         this.list.show();
18629         
18630         this.restrictHeight();
18631         
18632         if(this.tickable){
18633             
18634             this.tickItems = Roo.apply([], this.item);
18635             
18636             this.okBtn.show();
18637             this.cancelBtn.show();
18638             this.trigger.hide();
18639             
18640             if(this.editable){
18641                 this.tickableInputEl().focus();
18642             }
18643             
18644         }
18645         
18646         Roo.get(document).on('mousedown', this.collapseIf, this);
18647         Roo.get(document).on('mousewheel', this.collapseIf, this);
18648         if (!this.editable) {
18649             Roo.get(document).on('keydown', this.listKeyPress, this);
18650         }
18651         
18652         this.fireEvent('expand', this);
18653     },
18654
18655     // private
18656     // Implements the default empty TriggerField.onTriggerClick function
18657     onTriggerClick : function(e)
18658     {
18659         Roo.log('trigger click');
18660         
18661         if(this.disabled || !this.triggerList){
18662             return;
18663         }
18664         
18665         this.page = 0;
18666         this.loadNext = false;
18667         
18668         if(this.isExpanded()){
18669             this.collapse();
18670             if (!this.blockFocus) {
18671                 this.inputEl().focus();
18672             }
18673             
18674         }else {
18675             this.hasFocus = true;
18676             if(this.triggerAction == 'all') {
18677                 this.doQuery(this.allQuery, true);
18678             } else {
18679                 this.doQuery(this.getRawValue());
18680             }
18681             if (!this.blockFocus) {
18682                 this.inputEl().focus();
18683             }
18684         }
18685     },
18686     
18687     onTickableTriggerClick : function(e)
18688     {
18689         if(this.disabled){
18690             return;
18691         }
18692         
18693         this.page = 0;
18694         this.loadNext = false;
18695         this.hasFocus = true;
18696         
18697         if(this.triggerAction == 'all') {
18698             this.doQuery(this.allQuery, true);
18699         } else {
18700             this.doQuery(this.getRawValue());
18701         }
18702     },
18703     
18704     onSearchFieldClick : function(e)
18705     {
18706         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18707             this.onTickableFooterButtonClick(e, false, false);
18708             return;
18709         }
18710         
18711         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18712             return;
18713         }
18714         
18715         this.page = 0;
18716         this.loadNext = false;
18717         this.hasFocus = true;
18718         
18719         if(this.triggerAction == 'all') {
18720             this.doQuery(this.allQuery, true);
18721         } else {
18722             this.doQuery(this.getRawValue());
18723         }
18724     },
18725     
18726     listKeyPress : function(e)
18727     {
18728         //Roo.log('listkeypress');
18729         // scroll to first matching element based on key pres..
18730         if (e.isSpecialKey()) {
18731             return false;
18732         }
18733         var k = String.fromCharCode(e.getKey()).toUpperCase();
18734         //Roo.log(k);
18735         var match  = false;
18736         var csel = this.view.getSelectedNodes();
18737         var cselitem = false;
18738         if (csel.length) {
18739             var ix = this.view.indexOf(csel[0]);
18740             cselitem  = this.store.getAt(ix);
18741             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18742                 cselitem = false;
18743             }
18744             
18745         }
18746         
18747         this.store.each(function(v) { 
18748             if (cselitem) {
18749                 // start at existing selection.
18750                 if (cselitem.id == v.id) {
18751                     cselitem = false;
18752                 }
18753                 return true;
18754             }
18755                 
18756             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18757                 match = this.store.indexOf(v);
18758                 return false;
18759             }
18760             return true;
18761         }, this);
18762         
18763         if (match === false) {
18764             return true; // no more action?
18765         }
18766         // scroll to?
18767         this.view.select(match);
18768         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18769         sn.scrollIntoView(sn.dom.parentNode, false);
18770     },
18771     
18772     onViewScroll : function(e, t){
18773         
18774         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){
18775             return;
18776         }
18777         
18778         this.hasQuery = true;
18779         
18780         this.loading = this.list.select('.loading', true).first();
18781         
18782         if(this.loading === null){
18783             this.list.createChild({
18784                 tag: 'div',
18785                 cls: 'loading roo-select2-more-results roo-select2-active',
18786                 html: 'Loading more results...'
18787             });
18788             
18789             this.loading = this.list.select('.loading', true).first();
18790             
18791             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18792             
18793             this.loading.hide();
18794         }
18795         
18796         this.loading.show();
18797         
18798         var _combo = this;
18799         
18800         this.page++;
18801         this.loadNext = true;
18802         
18803         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18804         
18805         return;
18806     },
18807     
18808     addItem : function(o)
18809     {   
18810         var dv = ''; // display value
18811         
18812         if (this.displayField) {
18813             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18814         } else {
18815             // this is an error condition!!!
18816             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18817         }
18818         
18819         if(!dv.length){
18820             return;
18821         }
18822         
18823         var choice = this.choices.createChild({
18824             tag: 'li',
18825             cls: 'roo-select2-search-choice',
18826             cn: [
18827                 {
18828                     tag: 'div',
18829                     html: dv
18830                 },
18831                 {
18832                     tag: 'a',
18833                     href: '#',
18834                     cls: 'roo-select2-search-choice-close fa fa-times',
18835                     tabindex: '-1'
18836                 }
18837             ]
18838             
18839         }, this.searchField);
18840         
18841         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18842         
18843         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18844         
18845         this.item.push(o);
18846         
18847         this.lastData = o;
18848         
18849         this.syncValue();
18850         
18851         this.inputEl().dom.value = '';
18852         
18853         this.validate();
18854     },
18855     
18856     onRemoveItem : function(e, _self, o)
18857     {
18858         e.preventDefault();
18859         
18860         this.lastItem = Roo.apply([], this.item);
18861         
18862         var index = this.item.indexOf(o.data) * 1;
18863         
18864         if( index < 0){
18865             Roo.log('not this item?!');
18866             return;
18867         }
18868         
18869         this.item.splice(index, 1);
18870         o.item.remove();
18871         
18872         this.syncValue();
18873         
18874         this.fireEvent('remove', this, e);
18875         
18876         this.validate();
18877         
18878     },
18879     
18880     syncValue : function()
18881     {
18882         if(!this.item.length){
18883             this.clearValue();
18884             return;
18885         }
18886             
18887         var value = [];
18888         var _this = this;
18889         Roo.each(this.item, function(i){
18890             if(_this.valueField){
18891                 value.push(i[_this.valueField]);
18892                 return;
18893             }
18894
18895             value.push(i);
18896         });
18897
18898         this.value = value.join(',');
18899
18900         if(this.hiddenField){
18901             this.hiddenField.dom.value = this.value;
18902         }
18903         
18904         this.store.fireEvent("datachanged", this.store);
18905         
18906         this.validate();
18907     },
18908     
18909     clearItem : function()
18910     {
18911         if(!this.multiple){
18912             return;
18913         }
18914         
18915         this.item = [];
18916         
18917         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18918            c.remove();
18919         });
18920         
18921         this.syncValue();
18922         
18923         this.validate();
18924         
18925         if(this.tickable && !Roo.isTouch){
18926             this.view.refresh();
18927         }
18928     },
18929     
18930     inputEl: function ()
18931     {
18932         if(Roo.isIOS && this.useNativeIOS){
18933             return this.el.select('select.roo-ios-select', true).first();
18934         }
18935         
18936         if(Roo.isTouch && this.mobileTouchView){
18937             return this.el.select('input.form-control',true).first();
18938         }
18939         
18940         if(this.tickable){
18941             return this.searchField;
18942         }
18943         
18944         return this.el.select('input.form-control',true).first();
18945     },
18946     
18947     onTickableFooterButtonClick : function(e, btn, el)
18948     {
18949         e.preventDefault();
18950         
18951         this.lastItem = Roo.apply([], this.item);
18952         
18953         if(btn && btn.name == 'cancel'){
18954             this.tickItems = Roo.apply([], this.item);
18955             this.collapse();
18956             return;
18957         }
18958         
18959         this.clearItem();
18960         
18961         var _this = this;
18962         
18963         Roo.each(this.tickItems, function(o){
18964             _this.addItem(o);
18965         });
18966         
18967         this.collapse();
18968         
18969     },
18970     
18971     validate : function()
18972     {
18973         if(this.getVisibilityEl().hasClass('hidden')){
18974             return true;
18975         }
18976         
18977         var v = this.getRawValue();
18978         
18979         if(this.multiple){
18980             v = this.getValue();
18981         }
18982         
18983         if(this.disabled || this.allowBlank || v.length){
18984             this.markValid();
18985             return true;
18986         }
18987         
18988         this.markInvalid();
18989         return false;
18990     },
18991     
18992     tickableInputEl : function()
18993     {
18994         if(!this.tickable || !this.editable){
18995             return this.inputEl();
18996         }
18997         
18998         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18999     },
19000     
19001     
19002     getAutoCreateTouchView : function()
19003     {
19004         var id = Roo.id();
19005         
19006         var cfg = {
19007             cls: 'form-group' //input-group
19008         };
19009         
19010         var input =  {
19011             tag: 'input',
19012             id : id,
19013             type : this.inputType,
19014             cls : 'form-control x-combo-noedit',
19015             autocomplete: 'new-password',
19016             placeholder : this.placeholder || '',
19017             readonly : true
19018         };
19019         
19020         if (this.name) {
19021             input.name = this.name;
19022         }
19023         
19024         if (this.size) {
19025             input.cls += ' input-' + this.size;
19026         }
19027         
19028         if (this.disabled) {
19029             input.disabled = true;
19030         }
19031         
19032         var inputblock = {
19033             cls : 'roo-combobox-wrap',
19034             cn : [
19035                 input
19036             ]
19037         };
19038         
19039         if(this.before){
19040             inputblock.cls += ' input-group';
19041             
19042             inputblock.cn.unshift({
19043                 tag :'span',
19044                 cls : 'input-group-addon input-group-prepend input-group-text',
19045                 html : this.before
19046             });
19047         }
19048         
19049         if(this.removable && !this.multiple){
19050             inputblock.cls += ' roo-removable';
19051             
19052             inputblock.cn.push({
19053                 tag: 'button',
19054                 html : 'x',
19055                 cls : 'roo-combo-removable-btn close'
19056             });
19057         }
19058
19059         if(this.hasFeedback && !this.allowBlank){
19060             
19061             inputblock.cls += ' has-feedback';
19062             
19063             inputblock.cn.push({
19064                 tag: 'span',
19065                 cls: 'glyphicon form-control-feedback'
19066             });
19067             
19068         }
19069         
19070         if (this.after) {
19071             
19072             inputblock.cls += (this.before) ? '' : ' input-group';
19073             
19074             inputblock.cn.push({
19075                 tag :'span',
19076                 cls : 'input-group-addon input-group-append input-group-text',
19077                 html : this.after
19078             });
19079         }
19080
19081         
19082         var ibwrap = inputblock;
19083         
19084         if(this.multiple){
19085             ibwrap = {
19086                 tag: 'ul',
19087                 cls: 'roo-select2-choices',
19088                 cn:[
19089                     {
19090                         tag: 'li',
19091                         cls: 'roo-select2-search-field',
19092                         cn: [
19093
19094                             inputblock
19095                         ]
19096                     }
19097                 ]
19098             };
19099         
19100             
19101         }
19102         
19103         var combobox = {
19104             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19105             cn: [
19106                 {
19107                     tag: 'input',
19108                     type : 'hidden',
19109                     cls: 'form-hidden-field'
19110                 },
19111                 ibwrap
19112             ]
19113         };
19114         
19115         if(!this.multiple && this.showToggleBtn){
19116             
19117             var caret = {
19118                 cls: 'caret'
19119             };
19120             
19121             if (this.caret != false) {
19122                 caret = {
19123                      tag: 'i',
19124                      cls: 'fa fa-' + this.caret
19125                 };
19126                 
19127             }
19128             
19129             combobox.cn.push({
19130                 tag :'span',
19131                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19132                 cn : [
19133                     Roo.bootstrap.version == 3 ? caret : '',
19134                     {
19135                         tag: 'span',
19136                         cls: 'combobox-clear',
19137                         cn  : [
19138                             {
19139                                 tag : 'i',
19140                                 cls: 'icon-remove'
19141                             }
19142                         ]
19143                     }
19144                 ]
19145
19146             })
19147         }
19148         
19149         if(this.multiple){
19150             combobox.cls += ' roo-select2-container-multi';
19151         }
19152         
19153         var required =  this.allowBlank ?  {
19154                     tag : 'i',
19155                     style: 'display: none'
19156                 } : {
19157                    tag : 'i',
19158                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19159                    tooltip : 'This field is required'
19160                 };
19161         
19162         var align = this.labelAlign || this.parentLabelAlign();
19163         
19164         if (align ==='left' && this.fieldLabel.length) {
19165
19166             cfg.cn = [
19167                 required,
19168                 {
19169                     tag: 'label',
19170                     cls : 'control-label col-form-label',
19171                     html : this.fieldLabel
19172
19173                 },
19174                 {
19175                     cls : 'roo-combobox-wrap ', 
19176                     cn: [
19177                         combobox
19178                     ]
19179                 }
19180             ];
19181             
19182             var labelCfg = cfg.cn[1];
19183             var contentCfg = cfg.cn[2];
19184             
19185
19186             if(this.indicatorpos == 'right'){
19187                 cfg.cn = [
19188                     {
19189                         tag: 'label',
19190                         'for' :  id,
19191                         cls : 'control-label col-form-label',
19192                         cn : [
19193                             {
19194                                 tag : 'span',
19195                                 html : this.fieldLabel
19196                             },
19197                             required
19198                         ]
19199                     },
19200                     {
19201                         cls : "roo-combobox-wrap ",
19202                         cn: [
19203                             combobox
19204                         ]
19205                     }
19206
19207                 ];
19208                 
19209                 labelCfg = cfg.cn[0];
19210                 contentCfg = cfg.cn[1];
19211             }
19212             
19213            
19214             
19215             if(this.labelWidth > 12){
19216                 labelCfg.style = "width: " + this.labelWidth + 'px';
19217             }
19218            
19219             if(this.labelWidth < 13 && this.labelmd == 0){
19220                 this.labelmd = this.labelWidth;
19221             }
19222             
19223             if(this.labellg > 0){
19224                 labelCfg.cls += ' col-lg-' + this.labellg;
19225                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19226             }
19227             
19228             if(this.labelmd > 0){
19229                 labelCfg.cls += ' col-md-' + this.labelmd;
19230                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19231             }
19232             
19233             if(this.labelsm > 0){
19234                 labelCfg.cls += ' col-sm-' + this.labelsm;
19235                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19236             }
19237             
19238             if(this.labelxs > 0){
19239                 labelCfg.cls += ' col-xs-' + this.labelxs;
19240                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19241             }
19242                 
19243                 
19244         } else if ( this.fieldLabel.length) {
19245             cfg.cn = [
19246                required,
19247                 {
19248                     tag: 'label',
19249                     cls : 'control-label',
19250                     html : this.fieldLabel
19251
19252                 },
19253                 {
19254                     cls : '', 
19255                     cn: [
19256                         combobox
19257                     ]
19258                 }
19259             ];
19260             
19261             if(this.indicatorpos == 'right'){
19262                 cfg.cn = [
19263                     {
19264                         tag: 'label',
19265                         cls : 'control-label',
19266                         html : this.fieldLabel,
19267                         cn : [
19268                             required
19269                         ]
19270                     },
19271                     {
19272                         cls : '', 
19273                         cn: [
19274                             combobox
19275                         ]
19276                     }
19277                 ];
19278             }
19279         } else {
19280             cfg.cn = combobox;    
19281         }
19282         
19283         
19284         var settings = this;
19285         
19286         ['xs','sm','md','lg'].map(function(size){
19287             if (settings[size]) {
19288                 cfg.cls += ' col-' + size + '-' + settings[size];
19289             }
19290         });
19291         
19292         return cfg;
19293     },
19294     
19295     initTouchView : function()
19296     {
19297         this.renderTouchView();
19298         
19299         this.touchViewEl.on('scroll', function(){
19300             this.el.dom.scrollTop = 0;
19301         }, this);
19302         
19303         this.originalValue = this.getValue();
19304         
19305         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19306         
19307         this.inputEl().on("click", this.showTouchView, this);
19308         if (this.triggerEl) {
19309             this.triggerEl.on("click", this.showTouchView, this);
19310         }
19311         
19312         
19313         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19314         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19315         
19316         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19317         
19318         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19319         this.store.on('load', this.onTouchViewLoad, this);
19320         this.store.on('loadexception', this.onTouchViewLoadException, this);
19321         
19322         if(this.hiddenName){
19323             
19324             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19325             
19326             this.hiddenField.dom.value =
19327                 this.hiddenValue !== undefined ? this.hiddenValue :
19328                 this.value !== undefined ? this.value : '';
19329         
19330             this.el.dom.removeAttribute('name');
19331             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19332         }
19333         
19334         if(this.multiple){
19335             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19336             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19337         }
19338         
19339         if(this.removable && !this.multiple){
19340             var close = this.closeTriggerEl();
19341             if(close){
19342                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19343                 close.on('click', this.removeBtnClick, this, close);
19344             }
19345         }
19346         /*
19347          * fix the bug in Safari iOS8
19348          */
19349         this.inputEl().on("focus", function(e){
19350             document.activeElement.blur();
19351         }, this);
19352         
19353         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19354         
19355         return;
19356         
19357         
19358     },
19359     
19360     renderTouchView : function()
19361     {
19362         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19363         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19364         
19365         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19366         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19367         
19368         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19369         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19370         this.touchViewBodyEl.setStyle('overflow', 'auto');
19371         
19372         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19373         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19374         
19375         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19376         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19377         
19378     },
19379     
19380     showTouchView : function()
19381     {
19382         if(this.disabled){
19383             return;
19384         }
19385         
19386         this.touchViewHeaderEl.hide();
19387
19388         if(this.modalTitle.length){
19389             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19390             this.touchViewHeaderEl.show();
19391         }
19392
19393         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19394         this.touchViewEl.show();
19395
19396         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19397         
19398         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19399         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19400
19401         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19402
19403         if(this.modalTitle.length){
19404             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19405         }
19406         
19407         this.touchViewBodyEl.setHeight(bodyHeight);
19408
19409         if(this.animate){
19410             var _this = this;
19411             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19412         }else{
19413             this.touchViewEl.addClass(['in','show']);
19414         }
19415         
19416         if(this._touchViewMask){
19417             Roo.get(document.body).addClass("x-body-masked");
19418             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19419             this._touchViewMask.setStyle('z-index', 10000);
19420             this._touchViewMask.addClass('show');
19421         }
19422         
19423         this.doTouchViewQuery();
19424         
19425     },
19426     
19427     hideTouchView : function()
19428     {
19429         this.touchViewEl.removeClass(['in','show']);
19430
19431         if(this.animate){
19432             var _this = this;
19433             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19434         }else{
19435             this.touchViewEl.setStyle('display', 'none');
19436         }
19437         
19438         if(this._touchViewMask){
19439             this._touchViewMask.removeClass('show');
19440             Roo.get(document.body).removeClass("x-body-masked");
19441         }
19442     },
19443     
19444     setTouchViewValue : function()
19445     {
19446         if(this.multiple){
19447             this.clearItem();
19448         
19449             var _this = this;
19450
19451             Roo.each(this.tickItems, function(o){
19452                 this.addItem(o);
19453             }, this);
19454         }
19455         
19456         this.hideTouchView();
19457     },
19458     
19459     doTouchViewQuery : function()
19460     {
19461         var qe = {
19462             query: '',
19463             forceAll: true,
19464             combo: this,
19465             cancel:false
19466         };
19467         
19468         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19469             return false;
19470         }
19471         
19472         if(!this.alwaysQuery || this.mode == 'local'){
19473             this.onTouchViewLoad();
19474             return;
19475         }
19476         
19477         this.store.load();
19478     },
19479     
19480     onTouchViewBeforeLoad : function(combo,opts)
19481     {
19482         return;
19483     },
19484
19485     // private
19486     onTouchViewLoad : function()
19487     {
19488         if(this.store.getCount() < 1){
19489             this.onTouchViewEmptyResults();
19490             return;
19491         }
19492         
19493         this.clearTouchView();
19494         
19495         var rawValue = this.getRawValue();
19496         
19497         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19498         
19499         this.tickItems = [];
19500         
19501         this.store.data.each(function(d, rowIndex){
19502             var row = this.touchViewListGroup.createChild(template);
19503             
19504             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19505                 row.addClass(d.data.cls);
19506             }
19507             
19508             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19509                 var cfg = {
19510                     data : d.data,
19511                     html : d.data[this.displayField]
19512                 };
19513                 
19514                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19515                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19516                 }
19517             }
19518             row.removeClass('selected');
19519             if(!this.multiple && this.valueField &&
19520                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19521             {
19522                 // radio buttons..
19523                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19524                 row.addClass('selected');
19525             }
19526             
19527             if(this.multiple && this.valueField &&
19528                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19529             {
19530                 
19531                 // checkboxes...
19532                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19533                 this.tickItems.push(d.data);
19534             }
19535             
19536             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19537             
19538         }, this);
19539         
19540         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19541         
19542         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19543
19544         if(this.modalTitle.length){
19545             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19546         }
19547
19548         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19549         
19550         if(this.mobile_restrict_height && listHeight < bodyHeight){
19551             this.touchViewBodyEl.setHeight(listHeight);
19552         }
19553         
19554         var _this = this;
19555         
19556         if(firstChecked && listHeight > bodyHeight){
19557             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19558         }
19559         
19560     },
19561     
19562     onTouchViewLoadException : function()
19563     {
19564         this.hideTouchView();
19565     },
19566     
19567     onTouchViewEmptyResults : function()
19568     {
19569         this.clearTouchView();
19570         
19571         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19572         
19573         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19574         
19575     },
19576     
19577     clearTouchView : function()
19578     {
19579         this.touchViewListGroup.dom.innerHTML = '';
19580     },
19581     
19582     onTouchViewClick : function(e, el, o)
19583     {
19584         e.preventDefault();
19585         
19586         var row = o.row;
19587         var rowIndex = o.rowIndex;
19588         
19589         var r = this.store.getAt(rowIndex);
19590         
19591         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19592             
19593             if(!this.multiple){
19594                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19595                     c.dom.removeAttribute('checked');
19596                 }, this);
19597
19598                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19599
19600                 this.setFromData(r.data);
19601
19602                 var close = this.closeTriggerEl();
19603
19604                 if(close){
19605                     close.show();
19606                 }
19607
19608                 this.hideTouchView();
19609
19610                 this.fireEvent('select', this, r, rowIndex);
19611
19612                 return;
19613             }
19614
19615             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19616                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19617                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19618                 return;
19619             }
19620
19621             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19622             this.addItem(r.data);
19623             this.tickItems.push(r.data);
19624         }
19625     },
19626     
19627     getAutoCreateNativeIOS : function()
19628     {
19629         var cfg = {
19630             cls: 'form-group' //input-group,
19631         };
19632         
19633         var combobox =  {
19634             tag: 'select',
19635             cls : 'roo-ios-select'
19636         };
19637         
19638         if (this.name) {
19639             combobox.name = this.name;
19640         }
19641         
19642         if (this.disabled) {
19643             combobox.disabled = true;
19644         }
19645         
19646         var settings = this;
19647         
19648         ['xs','sm','md','lg'].map(function(size){
19649             if (settings[size]) {
19650                 cfg.cls += ' col-' + size + '-' + settings[size];
19651             }
19652         });
19653         
19654         cfg.cn = combobox;
19655         
19656         return cfg;
19657         
19658     },
19659     
19660     initIOSView : function()
19661     {
19662         this.store.on('load', this.onIOSViewLoad, this);
19663         
19664         return;
19665     },
19666     
19667     onIOSViewLoad : function()
19668     {
19669         if(this.store.getCount() < 1){
19670             return;
19671         }
19672         
19673         this.clearIOSView();
19674         
19675         if(this.allowBlank) {
19676             
19677             var default_text = '-- SELECT --';
19678             
19679             if(this.placeholder.length){
19680                 default_text = this.placeholder;
19681             }
19682             
19683             if(this.emptyTitle.length){
19684                 default_text += ' - ' + this.emptyTitle + ' -';
19685             }
19686             
19687             var opt = this.inputEl().createChild({
19688                 tag: 'option',
19689                 value : 0,
19690                 html : default_text
19691             });
19692             
19693             var o = {};
19694             o[this.valueField] = 0;
19695             o[this.displayField] = default_text;
19696             
19697             this.ios_options.push({
19698                 data : o,
19699                 el : opt
19700             });
19701             
19702         }
19703         
19704         this.store.data.each(function(d, rowIndex){
19705             
19706             var html = '';
19707             
19708             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19709                 html = d.data[this.displayField];
19710             }
19711             
19712             var value = '';
19713             
19714             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19715                 value = d.data[this.valueField];
19716             }
19717             
19718             var option = {
19719                 tag: 'option',
19720                 value : value,
19721                 html : html
19722             };
19723             
19724             if(this.value == d.data[this.valueField]){
19725                 option['selected'] = true;
19726             }
19727             
19728             var opt = this.inputEl().createChild(option);
19729             
19730             this.ios_options.push({
19731                 data : d.data,
19732                 el : opt
19733             });
19734             
19735         }, this);
19736         
19737         this.inputEl().on('change', function(){
19738            this.fireEvent('select', this);
19739         }, this);
19740         
19741     },
19742     
19743     clearIOSView: function()
19744     {
19745         this.inputEl().dom.innerHTML = '';
19746         
19747         this.ios_options = [];
19748     },
19749     
19750     setIOSValue: function(v)
19751     {
19752         this.value = v;
19753         
19754         if(!this.ios_options){
19755             return;
19756         }
19757         
19758         Roo.each(this.ios_options, function(opts){
19759            
19760            opts.el.dom.removeAttribute('selected');
19761            
19762            if(opts.data[this.valueField] != v){
19763                return;
19764            }
19765            
19766            opts.el.dom.setAttribute('selected', true);
19767            
19768         }, this);
19769     }
19770
19771     /** 
19772     * @cfg {Boolean} grow 
19773     * @hide 
19774     */
19775     /** 
19776     * @cfg {Number} growMin 
19777     * @hide 
19778     */
19779     /** 
19780     * @cfg {Number} growMax 
19781     * @hide 
19782     */
19783     /**
19784      * @hide
19785      * @method autoSize
19786      */
19787 });
19788
19789 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19790     
19791     header : {
19792         tag: 'div',
19793         cls: 'modal-header',
19794         cn: [
19795             {
19796                 tag: 'h4',
19797                 cls: 'modal-title'
19798             }
19799         ]
19800     },
19801     
19802     body : {
19803         tag: 'div',
19804         cls: 'modal-body',
19805         cn: [
19806             {
19807                 tag: 'ul',
19808                 cls: 'list-group'
19809             }
19810         ]
19811     },
19812     
19813     listItemRadio : {
19814         tag: 'li',
19815         cls: 'list-group-item',
19816         cn: [
19817             {
19818                 tag: 'span',
19819                 cls: 'roo-combobox-list-group-item-value'
19820             },
19821             {
19822                 tag: 'div',
19823                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19824                 cn: [
19825                     {
19826                         tag: 'input',
19827                         type: 'radio'
19828                     },
19829                     {
19830                         tag: 'label'
19831                     }
19832                 ]
19833             }
19834         ]
19835     },
19836     
19837     listItemCheckbox : {
19838         tag: 'li',
19839         cls: 'list-group-item',
19840         cn: [
19841             {
19842                 tag: 'span',
19843                 cls: 'roo-combobox-list-group-item-value'
19844             },
19845             {
19846                 tag: 'div',
19847                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19848                 cn: [
19849                     {
19850                         tag: 'input',
19851                         type: 'checkbox'
19852                     },
19853                     {
19854                         tag: 'label'
19855                     }
19856                 ]
19857             }
19858         ]
19859     },
19860     
19861     emptyResult : {
19862         tag: 'div',
19863         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19864     },
19865     
19866     footer : {
19867         tag: 'div',
19868         cls: 'modal-footer',
19869         cn: [
19870             {
19871                 tag: 'div',
19872                 cls: 'row',
19873                 cn: [
19874                     {
19875                         tag: 'div',
19876                         cls: 'col-xs-6 text-left',
19877                         cn: {
19878                             tag: 'button',
19879                             cls: 'btn btn-danger roo-touch-view-cancel',
19880                             html: 'Cancel'
19881                         }
19882                     },
19883                     {
19884                         tag: 'div',
19885                         cls: 'col-xs-6 text-right',
19886                         cn: {
19887                             tag: 'button',
19888                             cls: 'btn btn-success roo-touch-view-ok',
19889                             html: 'OK'
19890                         }
19891                     }
19892                 ]
19893             }
19894         ]
19895         
19896     }
19897 });
19898
19899 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19900     
19901     touchViewTemplate : {
19902         tag: 'div',
19903         cls: 'modal fade roo-combobox-touch-view',
19904         cn: [
19905             {
19906                 tag: 'div',
19907                 cls: 'modal-dialog',
19908                 style : 'position:fixed', // we have to fix position....
19909                 cn: [
19910                     {
19911                         tag: 'div',
19912                         cls: 'modal-content',
19913                         cn: [
19914                             Roo.bootstrap.form.ComboBox.header,
19915                             Roo.bootstrap.form.ComboBox.body,
19916                             Roo.bootstrap.form.ComboBox.footer
19917                         ]
19918                     }
19919                 ]
19920             }
19921         ]
19922     }
19923 });/*
19924  * Based on:
19925  * Ext JS Library 1.1.1
19926  * Copyright(c) 2006-2007, Ext JS, LLC.
19927  *
19928  * Originally Released Under LGPL - original licence link has changed is not relivant.
19929  *
19930  * Fork - LGPL
19931  * <script type="text/javascript">
19932  */
19933
19934 /**
19935  * @class Roo.View
19936  * @extends Roo.util.Observable
19937  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19938  * This class also supports single and multi selection modes. <br>
19939  * Create a data model bound view:
19940  <pre><code>
19941  var store = new Roo.data.Store(...);
19942
19943  var view = new Roo.View({
19944     el : "my-element",
19945     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19946  
19947     singleSelect: true,
19948     selectedClass: "ydataview-selected",
19949     store: store
19950  });
19951
19952  // listen for node click?
19953  view.on("click", function(vw, index, node, e){
19954  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19955  });
19956
19957  // load XML data
19958  dataModel.load("foobar.xml");
19959  </code></pre>
19960  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19961  * <br><br>
19962  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19963  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19964  * 
19965  * Note: old style constructor is still suported (container, template, config)
19966  * 
19967  * @constructor
19968  * Create a new View
19969  * @param {Object} config The config object
19970  * 
19971  */
19972 Roo.View = function(config, depreciated_tpl, depreciated_config){
19973     
19974     this.parent = false;
19975     
19976     if (typeof(depreciated_tpl) == 'undefined') {
19977         // new way.. - universal constructor.
19978         Roo.apply(this, config);
19979         this.el  = Roo.get(this.el);
19980     } else {
19981         // old format..
19982         this.el  = Roo.get(config);
19983         this.tpl = depreciated_tpl;
19984         Roo.apply(this, depreciated_config);
19985     }
19986     this.wrapEl  = this.el.wrap().wrap();
19987     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19988     
19989     
19990     if(typeof(this.tpl) == "string"){
19991         this.tpl = new Roo.Template(this.tpl);
19992     } else {
19993         // support xtype ctors..
19994         this.tpl = new Roo.factory(this.tpl, Roo);
19995     }
19996     
19997     
19998     this.tpl.compile();
19999     
20000     /** @private */
20001     this.addEvents({
20002         /**
20003          * @event beforeclick
20004          * Fires before a click is processed. Returns false to cancel the default action.
20005          * @param {Roo.View} this
20006          * @param {Number} index The index of the target node
20007          * @param {HTMLElement} node The target node
20008          * @param {Roo.EventObject} e The raw event object
20009          */
20010             "beforeclick" : true,
20011         /**
20012          * @event click
20013          * Fires when a template node is clicked.
20014          * @param {Roo.View} this
20015          * @param {Number} index The index of the target node
20016          * @param {HTMLElement} node The target node
20017          * @param {Roo.EventObject} e The raw event object
20018          */
20019             "click" : true,
20020         /**
20021          * @event dblclick
20022          * Fires when a template node is double clicked.
20023          * @param {Roo.View} this
20024          * @param {Number} index The index of the target node
20025          * @param {HTMLElement} node The target node
20026          * @param {Roo.EventObject} e The raw event object
20027          */
20028             "dblclick" : true,
20029         /**
20030          * @event contextmenu
20031          * Fires when a template node is right clicked.
20032          * @param {Roo.View} this
20033          * @param {Number} index The index of the target node
20034          * @param {HTMLElement} node The target node
20035          * @param {Roo.EventObject} e The raw event object
20036          */
20037             "contextmenu" : true,
20038         /**
20039          * @event selectionchange
20040          * Fires when the selected nodes change.
20041          * @param {Roo.View} this
20042          * @param {Array} selections Array of the selected nodes
20043          */
20044             "selectionchange" : true,
20045     
20046         /**
20047          * @event beforeselect
20048          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20049          * @param {Roo.View} this
20050          * @param {HTMLElement} node The node to be selected
20051          * @param {Array} selections Array of currently selected nodes
20052          */
20053             "beforeselect" : true,
20054         /**
20055          * @event preparedata
20056          * Fires on every row to render, to allow you to change the data.
20057          * @param {Roo.View} this
20058          * @param {Object} data to be rendered (change this)
20059          */
20060           "preparedata" : true
20061           
20062           
20063         });
20064
20065
20066
20067     this.el.on({
20068         "click": this.onClick,
20069         "dblclick": this.onDblClick,
20070         "contextmenu": this.onContextMenu,
20071         scope:this
20072     });
20073
20074     this.selections = [];
20075     this.nodes = [];
20076     this.cmp = new Roo.CompositeElementLite([]);
20077     if(this.store){
20078         this.store = Roo.factory(this.store, Roo.data);
20079         this.setStore(this.store, true);
20080     }
20081     
20082     if ( this.footer && this.footer.xtype) {
20083            
20084          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20085         
20086         this.footer.dataSource = this.store;
20087         this.footer.container = fctr;
20088         this.footer = Roo.factory(this.footer, Roo);
20089         fctr.insertFirst(this.el);
20090         
20091         // this is a bit insane - as the paging toolbar seems to detach the el..
20092 //        dom.parentNode.parentNode.parentNode
20093          // they get detached?
20094     }
20095     
20096     
20097     Roo.View.superclass.constructor.call(this);
20098     
20099     
20100 };
20101
20102 Roo.extend(Roo.View, Roo.util.Observable, {
20103     
20104      /**
20105      * @cfg {Roo.data.Store} store Data store to load data from.
20106      */
20107     store : false,
20108     
20109     /**
20110      * @cfg {String|Roo.Element} el The container element.
20111      */
20112     el : '',
20113     
20114     /**
20115      * @cfg {String|Roo.Template} tpl The template used by this View 
20116      */
20117     tpl : false,
20118     /**
20119      * @cfg {String} dataName the named area of the template to use as the data area
20120      *                          Works with domtemplates roo-name="name"
20121      */
20122     dataName: false,
20123     /**
20124      * @cfg {String} selectedClass The css class to add to selected nodes
20125      */
20126     selectedClass : "x-view-selected",
20127      /**
20128      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20129      */
20130     emptyText : "",
20131     
20132     /**
20133      * @cfg {String} text to display on mask (default Loading)
20134      */
20135     mask : false,
20136     /**
20137      * @cfg {Boolean} multiSelect Allow multiple selection
20138      */
20139     multiSelect : false,
20140     /**
20141      * @cfg {Boolean} singleSelect Allow single selection
20142      */
20143     singleSelect:  false,
20144     
20145     /**
20146      * @cfg {Boolean} toggleSelect - selecting 
20147      */
20148     toggleSelect : false,
20149     
20150     /**
20151      * @cfg {Boolean} tickable - selecting 
20152      */
20153     tickable : false,
20154     
20155     /**
20156      * Returns the element this view is bound to.
20157      * @return {Roo.Element}
20158      */
20159     getEl : function(){
20160         return this.wrapEl;
20161     },
20162     
20163     
20164
20165     /**
20166      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20167      */
20168     refresh : function(){
20169         //Roo.log('refresh');
20170         var t = this.tpl;
20171         
20172         // if we are using something like 'domtemplate', then
20173         // the what gets used is:
20174         // t.applySubtemplate(NAME, data, wrapping data..)
20175         // the outer template then get' applied with
20176         //     the store 'extra data'
20177         // and the body get's added to the
20178         //      roo-name="data" node?
20179         //      <span class='roo-tpl-{name}'></span> ?????
20180         
20181         
20182         
20183         this.clearSelections();
20184         this.el.update("");
20185         var html = [];
20186         var records = this.store.getRange();
20187         if(records.length < 1) {
20188             
20189             // is this valid??  = should it render a template??
20190             
20191             this.el.update(this.emptyText);
20192             return;
20193         }
20194         var el = this.el;
20195         if (this.dataName) {
20196             this.el.update(t.apply(this.store.meta)); //????
20197             el = this.el.child('.roo-tpl-' + this.dataName);
20198         }
20199         
20200         for(var i = 0, len = records.length; i < len; i++){
20201             var data = this.prepareData(records[i].data, i, records[i]);
20202             this.fireEvent("preparedata", this, data, i, records[i]);
20203             
20204             var d = Roo.apply({}, data);
20205             
20206             if(this.tickable){
20207                 Roo.apply(d, {'roo-id' : Roo.id()});
20208                 
20209                 var _this = this;
20210             
20211                 Roo.each(this.parent.item, function(item){
20212                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20213                         return;
20214                     }
20215                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20216                 });
20217             }
20218             
20219             html[html.length] = Roo.util.Format.trim(
20220                 this.dataName ?
20221                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20222                     t.apply(d)
20223             );
20224         }
20225         
20226         
20227         
20228         el.update(html.join(""));
20229         this.nodes = el.dom.childNodes;
20230         this.updateIndexes(0);
20231     },
20232     
20233
20234     /**
20235      * Function to override to reformat the data that is sent to
20236      * the template for each node.
20237      * DEPRICATED - use the preparedata event handler.
20238      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20239      * a JSON object for an UpdateManager bound view).
20240      */
20241     prepareData : function(data, index, record)
20242     {
20243         this.fireEvent("preparedata", this, data, index, record);
20244         return data;
20245     },
20246
20247     onUpdate : function(ds, record){
20248         // Roo.log('on update');   
20249         this.clearSelections();
20250         var index = this.store.indexOf(record);
20251         var n = this.nodes[index];
20252         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20253         n.parentNode.removeChild(n);
20254         this.updateIndexes(index, index);
20255     },
20256
20257     
20258     
20259 // --------- FIXME     
20260     onAdd : function(ds, records, index)
20261     {
20262         //Roo.log(['on Add', ds, records, index] );        
20263         this.clearSelections();
20264         if(this.nodes.length == 0){
20265             this.refresh();
20266             return;
20267         }
20268         var n = this.nodes[index];
20269         for(var i = 0, len = records.length; i < len; i++){
20270             var d = this.prepareData(records[i].data, i, records[i]);
20271             if(n){
20272                 this.tpl.insertBefore(n, d);
20273             }else{
20274                 
20275                 this.tpl.append(this.el, d);
20276             }
20277         }
20278         this.updateIndexes(index);
20279     },
20280
20281     onRemove : function(ds, record, index){
20282        // Roo.log('onRemove');
20283         this.clearSelections();
20284         var el = this.dataName  ?
20285             this.el.child('.roo-tpl-' + this.dataName) :
20286             this.el; 
20287         
20288         el.dom.removeChild(this.nodes[index]);
20289         this.updateIndexes(index);
20290     },
20291
20292     /**
20293      * Refresh an individual node.
20294      * @param {Number} index
20295      */
20296     refreshNode : function(index){
20297         this.onUpdate(this.store, this.store.getAt(index));
20298     },
20299
20300     updateIndexes : function(startIndex, endIndex){
20301         var ns = this.nodes;
20302         startIndex = startIndex || 0;
20303         endIndex = endIndex || ns.length - 1;
20304         for(var i = startIndex; i <= endIndex; i++){
20305             ns[i].nodeIndex = i;
20306         }
20307     },
20308
20309     /**
20310      * Changes the data store this view uses and refresh the view.
20311      * @param {Store} store
20312      */
20313     setStore : function(store, initial){
20314         if(!initial && this.store){
20315             this.store.un("datachanged", this.refresh);
20316             this.store.un("add", this.onAdd);
20317             this.store.un("remove", this.onRemove);
20318             this.store.un("update", this.onUpdate);
20319             this.store.un("clear", this.refresh);
20320             this.store.un("beforeload", this.onBeforeLoad);
20321             this.store.un("load", this.onLoad);
20322             this.store.un("loadexception", this.onLoad);
20323         }
20324         if(store){
20325           
20326             store.on("datachanged", this.refresh, this);
20327             store.on("add", this.onAdd, this);
20328             store.on("remove", this.onRemove, this);
20329             store.on("update", this.onUpdate, this);
20330             store.on("clear", this.refresh, this);
20331             store.on("beforeload", this.onBeforeLoad, this);
20332             store.on("load", this.onLoad, this);
20333             store.on("loadexception", this.onLoad, this);
20334         }
20335         
20336         if(store){
20337             this.refresh();
20338         }
20339     },
20340     /**
20341      * onbeforeLoad - masks the loading area.
20342      *
20343      */
20344     onBeforeLoad : function(store,opts)
20345     {
20346          //Roo.log('onBeforeLoad');   
20347         if (!opts.add) {
20348             this.el.update("");
20349         }
20350         this.el.mask(this.mask ? this.mask : "Loading" ); 
20351     },
20352     onLoad : function ()
20353     {
20354         this.el.unmask();
20355     },
20356     
20357
20358     /**
20359      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20360      * @param {HTMLElement} node
20361      * @return {HTMLElement} The template node
20362      */
20363     findItemFromChild : function(node){
20364         var el = this.dataName  ?
20365             this.el.child('.roo-tpl-' + this.dataName,true) :
20366             this.el.dom; 
20367         
20368         if(!node || node.parentNode == el){
20369                     return node;
20370             }
20371             var p = node.parentNode;
20372             while(p && p != el){
20373             if(p.parentNode == el){
20374                 return p;
20375             }
20376             p = p.parentNode;
20377         }
20378             return null;
20379     },
20380
20381     /** @ignore */
20382     onClick : function(e){
20383         var item = this.findItemFromChild(e.getTarget());
20384         if(item){
20385             var index = this.indexOf(item);
20386             if(this.onItemClick(item, index, e) !== false){
20387                 this.fireEvent("click", this, index, item, e);
20388             }
20389         }else{
20390             this.clearSelections();
20391         }
20392     },
20393
20394     /** @ignore */
20395     onContextMenu : function(e){
20396         var item = this.findItemFromChild(e.getTarget());
20397         if(item){
20398             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20399         }
20400     },
20401
20402     /** @ignore */
20403     onDblClick : function(e){
20404         var item = this.findItemFromChild(e.getTarget());
20405         if(item){
20406             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20407         }
20408     },
20409
20410     onItemClick : function(item, index, e)
20411     {
20412         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20413             return false;
20414         }
20415         if (this.toggleSelect) {
20416             var m = this.isSelected(item) ? 'unselect' : 'select';
20417             //Roo.log(m);
20418             var _t = this;
20419             _t[m](item, true, false);
20420             return true;
20421         }
20422         if(this.multiSelect || this.singleSelect){
20423             if(this.multiSelect && e.shiftKey && this.lastSelection){
20424                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20425             }else{
20426                 this.select(item, this.multiSelect && e.ctrlKey);
20427                 this.lastSelection = item;
20428             }
20429             
20430             if(!this.tickable){
20431                 e.preventDefault();
20432             }
20433             
20434         }
20435         return true;
20436     },
20437
20438     /**
20439      * Get the number of selected nodes.
20440      * @return {Number}
20441      */
20442     getSelectionCount : function(){
20443         return this.selections.length;
20444     },
20445
20446     /**
20447      * Get the currently selected nodes.
20448      * @return {Array} An array of HTMLElements
20449      */
20450     getSelectedNodes : function(){
20451         return this.selections;
20452     },
20453
20454     /**
20455      * Get the indexes of the selected nodes.
20456      * @return {Array}
20457      */
20458     getSelectedIndexes : function(){
20459         var indexes = [], s = this.selections;
20460         for(var i = 0, len = s.length; i < len; i++){
20461             indexes.push(s[i].nodeIndex);
20462         }
20463         return indexes;
20464     },
20465
20466     /**
20467      * Clear all selections
20468      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20469      */
20470     clearSelections : function(suppressEvent){
20471         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20472             this.cmp.elements = this.selections;
20473             this.cmp.removeClass(this.selectedClass);
20474             this.selections = [];
20475             if(!suppressEvent){
20476                 this.fireEvent("selectionchange", this, this.selections);
20477             }
20478         }
20479     },
20480
20481     /**
20482      * Returns true if the passed node is selected
20483      * @param {HTMLElement/Number} node The node or node index
20484      * @return {Boolean}
20485      */
20486     isSelected : function(node){
20487         var s = this.selections;
20488         if(s.length < 1){
20489             return false;
20490         }
20491         node = this.getNode(node);
20492         return s.indexOf(node) !== -1;
20493     },
20494
20495     /**
20496      * Selects nodes.
20497      * @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
20498      * @param {Boolean} keepExisting (optional) true to keep existing selections
20499      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20500      */
20501     select : function(nodeInfo, keepExisting, suppressEvent){
20502         if(nodeInfo instanceof Array){
20503             if(!keepExisting){
20504                 this.clearSelections(true);
20505             }
20506             for(var i = 0, len = nodeInfo.length; i < len; i++){
20507                 this.select(nodeInfo[i], true, true);
20508             }
20509             return;
20510         } 
20511         var node = this.getNode(nodeInfo);
20512         if(!node || this.isSelected(node)){
20513             return; // already selected.
20514         }
20515         if(!keepExisting){
20516             this.clearSelections(true);
20517         }
20518         
20519         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20520             Roo.fly(node).addClass(this.selectedClass);
20521             this.selections.push(node);
20522             if(!suppressEvent){
20523                 this.fireEvent("selectionchange", this, this.selections);
20524             }
20525         }
20526         
20527         
20528     },
20529       /**
20530      * Unselects nodes.
20531      * @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
20532      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20533      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20534      */
20535     unselect : function(nodeInfo, keepExisting, suppressEvent)
20536     {
20537         if(nodeInfo instanceof Array){
20538             Roo.each(this.selections, function(s) {
20539                 this.unselect(s, nodeInfo);
20540             }, this);
20541             return;
20542         }
20543         var node = this.getNode(nodeInfo);
20544         if(!node || !this.isSelected(node)){
20545             //Roo.log("not selected");
20546             return; // not selected.
20547         }
20548         // fireevent???
20549         var ns = [];
20550         Roo.each(this.selections, function(s) {
20551             if (s == node ) {
20552                 Roo.fly(node).removeClass(this.selectedClass);
20553
20554                 return;
20555             }
20556             ns.push(s);
20557         },this);
20558         
20559         this.selections= ns;
20560         this.fireEvent("selectionchange", this, this.selections);
20561     },
20562
20563     /**
20564      * Gets a template node.
20565      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20566      * @return {HTMLElement} The node or null if it wasn't found
20567      */
20568     getNode : function(nodeInfo){
20569         if(typeof nodeInfo == "string"){
20570             return document.getElementById(nodeInfo);
20571         }else if(typeof nodeInfo == "number"){
20572             return this.nodes[nodeInfo];
20573         }
20574         return nodeInfo;
20575     },
20576
20577     /**
20578      * Gets a range template nodes.
20579      * @param {Number} startIndex
20580      * @param {Number} endIndex
20581      * @return {Array} An array of nodes
20582      */
20583     getNodes : function(start, end){
20584         var ns = this.nodes;
20585         start = start || 0;
20586         end = typeof end == "undefined" ? ns.length - 1 : end;
20587         var nodes = [];
20588         if(start <= end){
20589             for(var i = start; i <= end; i++){
20590                 nodes.push(ns[i]);
20591             }
20592         } else{
20593             for(var i = start; i >= end; i--){
20594                 nodes.push(ns[i]);
20595             }
20596         }
20597         return nodes;
20598     },
20599
20600     /**
20601      * Finds the index of the passed node
20602      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20603      * @return {Number} The index of the node or -1
20604      */
20605     indexOf : function(node){
20606         node = this.getNode(node);
20607         if(typeof node.nodeIndex == "number"){
20608             return node.nodeIndex;
20609         }
20610         var ns = this.nodes;
20611         for(var i = 0, len = ns.length; i < len; i++){
20612             if(ns[i] == node){
20613                 return i;
20614             }
20615         }
20616         return -1;
20617     }
20618 });
20619 /*
20620  * - LGPL
20621  *
20622  * based on jquery fullcalendar
20623  * 
20624  */
20625
20626 Roo.bootstrap = Roo.bootstrap || {};
20627 /**
20628  * @class Roo.bootstrap.Calendar
20629  * @extends Roo.bootstrap.Component
20630  * Bootstrap Calendar class
20631  * @cfg {Boolean} loadMask (true|false) default false
20632  * @cfg {Object} header generate the user specific header of the calendar, default false
20633
20634  * @constructor
20635  * Create a new Container
20636  * @param {Object} config The config object
20637  */
20638
20639
20640
20641 Roo.bootstrap.Calendar = function(config){
20642     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20643      this.addEvents({
20644         /**
20645              * @event select
20646              * Fires when a date is selected
20647              * @param {DatePicker} this
20648              * @param {Date} date The selected date
20649              */
20650         'select': true,
20651         /**
20652              * @event monthchange
20653              * Fires when the displayed month changes 
20654              * @param {DatePicker} this
20655              * @param {Date} date The selected month
20656              */
20657         'monthchange': true,
20658         /**
20659              * @event evententer
20660              * Fires when mouse over an event
20661              * @param {Calendar} this
20662              * @param {event} Event
20663              */
20664         'evententer': true,
20665         /**
20666              * @event eventleave
20667              * Fires when the mouse leaves an
20668              * @param {Calendar} this
20669              * @param {event}
20670              */
20671         'eventleave': true,
20672         /**
20673              * @event eventclick
20674              * Fires when the mouse click an
20675              * @param {Calendar} this
20676              * @param {event}
20677              */
20678         'eventclick': true
20679         
20680     });
20681
20682 };
20683
20684 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20685     
20686           /**
20687      * @cfg {Roo.data.Store} store
20688      * The data source for the calendar
20689      */
20690         store : false,
20691      /**
20692      * @cfg {Number} startDay
20693      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20694      */
20695     startDay : 0,
20696     
20697     loadMask : false,
20698     
20699     header : false,
20700       
20701     getAutoCreate : function(){
20702         
20703         
20704         var fc_button = function(name, corner, style, content ) {
20705             return Roo.apply({},{
20706                 tag : 'span',
20707                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20708                          (corner.length ?
20709                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20710                             ''
20711                         ),
20712                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20713                 unselectable: 'on'
20714             });
20715         };
20716         
20717         var header = {};
20718         
20719         if(!this.header){
20720             header = {
20721                 tag : 'table',
20722                 cls : 'fc-header',
20723                 style : 'width:100%',
20724                 cn : [
20725                     {
20726                         tag: 'tr',
20727                         cn : [
20728                             {
20729                                 tag : 'td',
20730                                 cls : 'fc-header-left',
20731                                 cn : [
20732                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20733                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20734                                     { tag: 'span', cls: 'fc-header-space' },
20735                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20736
20737
20738                                 ]
20739                             },
20740
20741                             {
20742                                 tag : 'td',
20743                                 cls : 'fc-header-center',
20744                                 cn : [
20745                                     {
20746                                         tag: 'span',
20747                                         cls: 'fc-header-title',
20748                                         cn : {
20749                                             tag: 'H2',
20750                                             html : 'month / year'
20751                                         }
20752                                     }
20753
20754                                 ]
20755                             },
20756                             {
20757                                 tag : 'td',
20758                                 cls : 'fc-header-right',
20759                                 cn : [
20760                               /*      fc_button('month', 'left', '', 'month' ),
20761                                     fc_button('week', '', '', 'week' ),
20762                                     fc_button('day', 'right', '', 'day' )
20763                                 */    
20764
20765                                 ]
20766                             }
20767
20768                         ]
20769                     }
20770                 ]
20771             };
20772         }
20773         
20774         header = this.header;
20775         
20776        
20777         var cal_heads = function() {
20778             var ret = [];
20779             // fixme - handle this.
20780             
20781             for (var i =0; i < Date.dayNames.length; i++) {
20782                 var d = Date.dayNames[i];
20783                 ret.push({
20784                     tag: 'th',
20785                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20786                     html : d.substring(0,3)
20787                 });
20788                 
20789             }
20790             ret[0].cls += ' fc-first';
20791             ret[6].cls += ' fc-last';
20792             return ret;
20793         };
20794         var cal_cell = function(n) {
20795             return  {
20796                 tag: 'td',
20797                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20798                 cn : [
20799                     {
20800                         cn : [
20801                             {
20802                                 cls: 'fc-day-number',
20803                                 html: 'D'
20804                             },
20805                             {
20806                                 cls: 'fc-day-content',
20807                              
20808                                 cn : [
20809                                      {
20810                                         style: 'position: relative;' // height: 17px;
20811                                     }
20812                                 ]
20813                             }
20814                             
20815                             
20816                         ]
20817                     }
20818                 ]
20819                 
20820             }
20821         };
20822         var cal_rows = function() {
20823             
20824             var ret = [];
20825             for (var r = 0; r < 6; r++) {
20826                 var row= {
20827                     tag : 'tr',
20828                     cls : 'fc-week',
20829                     cn : []
20830                 };
20831                 
20832                 for (var i =0; i < Date.dayNames.length; i++) {
20833                     var d = Date.dayNames[i];
20834                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20835
20836                 }
20837                 row.cn[0].cls+=' fc-first';
20838                 row.cn[0].cn[0].style = 'min-height:90px';
20839                 row.cn[6].cls+=' fc-last';
20840                 ret.push(row);
20841                 
20842             }
20843             ret[0].cls += ' fc-first';
20844             ret[4].cls += ' fc-prev-last';
20845             ret[5].cls += ' fc-last';
20846             return ret;
20847             
20848         };
20849         
20850         var cal_table = {
20851             tag: 'table',
20852             cls: 'fc-border-separate',
20853             style : 'width:100%',
20854             cellspacing  : 0,
20855             cn : [
20856                 { 
20857                     tag: 'thead',
20858                     cn : [
20859                         { 
20860                             tag: 'tr',
20861                             cls : 'fc-first fc-last',
20862                             cn : cal_heads()
20863                         }
20864                     ]
20865                 },
20866                 { 
20867                     tag: 'tbody',
20868                     cn : cal_rows()
20869                 }
20870                   
20871             ]
20872         };
20873          
20874          var cfg = {
20875             cls : 'fc fc-ltr',
20876             cn : [
20877                 header,
20878                 {
20879                     cls : 'fc-content',
20880                     style : "position: relative;",
20881                     cn : [
20882                         {
20883                             cls : 'fc-view fc-view-month fc-grid',
20884                             style : 'position: relative',
20885                             unselectable : 'on',
20886                             cn : [
20887                                 {
20888                                     cls : 'fc-event-container',
20889                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20890                                 },
20891                                 cal_table
20892                             ]
20893                         }
20894                     ]
20895     
20896                 }
20897            ] 
20898             
20899         };
20900         
20901          
20902         
20903         return cfg;
20904     },
20905     
20906     
20907     initEvents : function()
20908     {
20909         if(!this.store){
20910             throw "can not find store for calendar";
20911         }
20912         
20913         var mark = {
20914             tag: "div",
20915             cls:"x-dlg-mask",
20916             style: "text-align:center",
20917             cn: [
20918                 {
20919                     tag: "div",
20920                     style: "background-color:white;width:50%;margin:250 auto",
20921                     cn: [
20922                         {
20923                             tag: "img",
20924                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20925                         },
20926                         {
20927                             tag: "span",
20928                             html: "Loading"
20929                         }
20930                         
20931                     ]
20932                 }
20933             ]
20934         };
20935         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20936         
20937         var size = this.el.select('.fc-content', true).first().getSize();
20938         this.maskEl.setSize(size.width, size.height);
20939         this.maskEl.enableDisplayMode("block");
20940         if(!this.loadMask){
20941             this.maskEl.hide();
20942         }
20943         
20944         this.store = Roo.factory(this.store, Roo.data);
20945         this.store.on('load', this.onLoad, this);
20946         this.store.on('beforeload', this.onBeforeLoad, this);
20947         
20948         this.resize();
20949         
20950         this.cells = this.el.select('.fc-day',true);
20951         //Roo.log(this.cells);
20952         this.textNodes = this.el.query('.fc-day-number');
20953         this.cells.addClassOnOver('fc-state-hover');
20954         
20955         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20956         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20957         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20958         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20959         
20960         this.on('monthchange', this.onMonthChange, this);
20961         
20962         this.update(new Date().clearTime());
20963     },
20964     
20965     resize : function() {
20966         var sz  = this.el.getSize();
20967         
20968         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20969         this.el.select('.fc-day-content div',true).setHeight(34);
20970     },
20971     
20972     
20973     // private
20974     showPrevMonth : function(e){
20975         this.update(this.activeDate.add("mo", -1));
20976     },
20977     showToday : function(e){
20978         this.update(new Date().clearTime());
20979     },
20980     // private
20981     showNextMonth : function(e){
20982         this.update(this.activeDate.add("mo", 1));
20983     },
20984
20985     // private
20986     showPrevYear : function(){
20987         this.update(this.activeDate.add("y", -1));
20988     },
20989
20990     // private
20991     showNextYear : function(){
20992         this.update(this.activeDate.add("y", 1));
20993     },
20994
20995     
20996    // private
20997     update : function(date)
20998     {
20999         var vd = this.activeDate;
21000         this.activeDate = date;
21001 //        if(vd && this.el){
21002 //            var t = date.getTime();
21003 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21004 //                Roo.log('using add remove');
21005 //                
21006 //                this.fireEvent('monthchange', this, date);
21007 //                
21008 //                this.cells.removeClass("fc-state-highlight");
21009 //                this.cells.each(function(c){
21010 //                   if(c.dateValue == t){
21011 //                       c.addClass("fc-state-highlight");
21012 //                       setTimeout(function(){
21013 //                            try{c.dom.firstChild.focus();}catch(e){}
21014 //                       }, 50);
21015 //                       return false;
21016 //                   }
21017 //                   return true;
21018 //                });
21019 //                return;
21020 //            }
21021 //        }
21022         
21023         var days = date.getDaysInMonth();
21024         
21025         var firstOfMonth = date.getFirstDateOfMonth();
21026         var startingPos = firstOfMonth.getDay()-this.startDay;
21027         
21028         if(startingPos < this.startDay){
21029             startingPos += 7;
21030         }
21031         
21032         var pm = date.add(Date.MONTH, -1);
21033         var prevStart = pm.getDaysInMonth()-startingPos;
21034 //        
21035         this.cells = this.el.select('.fc-day',true);
21036         this.textNodes = this.el.query('.fc-day-number');
21037         this.cells.addClassOnOver('fc-state-hover');
21038         
21039         var cells = this.cells.elements;
21040         var textEls = this.textNodes;
21041         
21042         Roo.each(cells, function(cell){
21043             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21044         });
21045         
21046         days += startingPos;
21047
21048         // convert everything to numbers so it's fast
21049         var day = 86400000;
21050         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21051         //Roo.log(d);
21052         //Roo.log(pm);
21053         //Roo.log(prevStart);
21054         
21055         var today = new Date().clearTime().getTime();
21056         var sel = date.clearTime().getTime();
21057         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21058         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21059         var ddMatch = this.disabledDatesRE;
21060         var ddText = this.disabledDatesText;
21061         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21062         var ddaysText = this.disabledDaysText;
21063         var format = this.format;
21064         
21065         var setCellClass = function(cal, cell){
21066             cell.row = 0;
21067             cell.events = [];
21068             cell.more = [];
21069             //Roo.log('set Cell Class');
21070             cell.title = "";
21071             var t = d.getTime();
21072             
21073             //Roo.log(d);
21074             
21075             cell.dateValue = t;
21076             if(t == today){
21077                 cell.className += " fc-today";
21078                 cell.className += " fc-state-highlight";
21079                 cell.title = cal.todayText;
21080             }
21081             if(t == sel){
21082                 // disable highlight in other month..
21083                 //cell.className += " fc-state-highlight";
21084                 
21085             }
21086             // disabling
21087             if(t < min) {
21088                 cell.className = " fc-state-disabled";
21089                 cell.title = cal.minText;
21090                 return;
21091             }
21092             if(t > max) {
21093                 cell.className = " fc-state-disabled";
21094                 cell.title = cal.maxText;
21095                 return;
21096             }
21097             if(ddays){
21098                 if(ddays.indexOf(d.getDay()) != -1){
21099                     cell.title = ddaysText;
21100                     cell.className = " fc-state-disabled";
21101                 }
21102             }
21103             if(ddMatch && format){
21104                 var fvalue = d.dateFormat(format);
21105                 if(ddMatch.test(fvalue)){
21106                     cell.title = ddText.replace("%0", fvalue);
21107                     cell.className = " fc-state-disabled";
21108                 }
21109             }
21110             
21111             if (!cell.initialClassName) {
21112                 cell.initialClassName = cell.dom.className;
21113             }
21114             
21115             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21116         };
21117
21118         var i = 0;
21119         
21120         for(; i < startingPos; i++) {
21121             textEls[i].innerHTML = (++prevStart);
21122             d.setDate(d.getDate()+1);
21123             
21124             cells[i].className = "fc-past fc-other-month";
21125             setCellClass(this, cells[i]);
21126         }
21127         
21128         var intDay = 0;
21129         
21130         for(; i < days; i++){
21131             intDay = i - startingPos + 1;
21132             textEls[i].innerHTML = (intDay);
21133             d.setDate(d.getDate()+1);
21134             
21135             cells[i].className = ''; // "x-date-active";
21136             setCellClass(this, cells[i]);
21137         }
21138         var extraDays = 0;
21139         
21140         for(; i < 42; i++) {
21141             textEls[i].innerHTML = (++extraDays);
21142             d.setDate(d.getDate()+1);
21143             
21144             cells[i].className = "fc-future fc-other-month";
21145             setCellClass(this, cells[i]);
21146         }
21147         
21148         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21149         
21150         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21151         
21152         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21153         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21154         
21155         if(totalRows != 6){
21156             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21157             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21158         }
21159         
21160         this.fireEvent('monthchange', this, date);
21161         
21162         
21163         /*
21164         if(!this.internalRender){
21165             var main = this.el.dom.firstChild;
21166             var w = main.offsetWidth;
21167             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21168             Roo.fly(main).setWidth(w);
21169             this.internalRender = true;
21170             // opera does not respect the auto grow header center column
21171             // then, after it gets a width opera refuses to recalculate
21172             // without a second pass
21173             if(Roo.isOpera && !this.secondPass){
21174                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21175                 this.secondPass = true;
21176                 this.update.defer(10, this, [date]);
21177             }
21178         }
21179         */
21180         
21181     },
21182     
21183     findCell : function(dt) {
21184         dt = dt.clearTime().getTime();
21185         var ret = false;
21186         this.cells.each(function(c){
21187             //Roo.log("check " +c.dateValue + '?=' + dt);
21188             if(c.dateValue == dt){
21189                 ret = c;
21190                 return false;
21191             }
21192             return true;
21193         });
21194         
21195         return ret;
21196     },
21197     
21198     findCells : function(ev) {
21199         var s = ev.start.clone().clearTime().getTime();
21200        // Roo.log(s);
21201         var e= ev.end.clone().clearTime().getTime();
21202        // Roo.log(e);
21203         var ret = [];
21204         this.cells.each(function(c){
21205              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21206             
21207             if(c.dateValue > e){
21208                 return ;
21209             }
21210             if(c.dateValue < s){
21211                 return ;
21212             }
21213             ret.push(c);
21214         });
21215         
21216         return ret;    
21217     },
21218     
21219 //    findBestRow: function(cells)
21220 //    {
21221 //        var ret = 0;
21222 //        
21223 //        for (var i =0 ; i < cells.length;i++) {
21224 //            ret  = Math.max(cells[i].rows || 0,ret);
21225 //        }
21226 //        return ret;
21227 //        
21228 //    },
21229     
21230     
21231     addItem : function(ev)
21232     {
21233         // look for vertical location slot in
21234         var cells = this.findCells(ev);
21235         
21236 //        ev.row = this.findBestRow(cells);
21237         
21238         // work out the location.
21239         
21240         var crow = false;
21241         var rows = [];
21242         for(var i =0; i < cells.length; i++) {
21243             
21244             cells[i].row = cells[0].row;
21245             
21246             if(i == 0){
21247                 cells[i].row = cells[i].row + 1;
21248             }
21249             
21250             if (!crow) {
21251                 crow = {
21252                     start : cells[i],
21253                     end :  cells[i]
21254                 };
21255                 continue;
21256             }
21257             if (crow.start.getY() == cells[i].getY()) {
21258                 // on same row.
21259                 crow.end = cells[i];
21260                 continue;
21261             }
21262             // different row.
21263             rows.push(crow);
21264             crow = {
21265                 start: cells[i],
21266                 end : cells[i]
21267             };
21268             
21269         }
21270         
21271         rows.push(crow);
21272         ev.els = [];
21273         ev.rows = rows;
21274         ev.cells = cells;
21275         
21276         cells[0].events.push(ev);
21277         
21278         this.calevents.push(ev);
21279     },
21280     
21281     clearEvents: function() {
21282         
21283         if(!this.calevents){
21284             return;
21285         }
21286         
21287         Roo.each(this.cells.elements, function(c){
21288             c.row = 0;
21289             c.events = [];
21290             c.more = [];
21291         });
21292         
21293         Roo.each(this.calevents, function(e) {
21294             Roo.each(e.els, function(el) {
21295                 el.un('mouseenter' ,this.onEventEnter, this);
21296                 el.un('mouseleave' ,this.onEventLeave, this);
21297                 el.remove();
21298             },this);
21299         },this);
21300         
21301         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21302             e.remove();
21303         });
21304         
21305     },
21306     
21307     renderEvents: function()
21308     {   
21309         var _this = this;
21310         
21311         this.cells.each(function(c) {
21312             
21313             if(c.row < 5){
21314                 return;
21315             }
21316             
21317             var ev = c.events;
21318             
21319             var r = 4;
21320             if(c.row != c.events.length){
21321                 r = 4 - (4 - (c.row - c.events.length));
21322             }
21323             
21324             c.events = ev.slice(0, r);
21325             c.more = ev.slice(r);
21326             
21327             if(c.more.length && c.more.length == 1){
21328                 c.events.push(c.more.pop());
21329             }
21330             
21331             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21332             
21333         });
21334             
21335         this.cells.each(function(c) {
21336             
21337             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21338             
21339             
21340             for (var e = 0; e < c.events.length; e++){
21341                 var ev = c.events[e];
21342                 var rows = ev.rows;
21343                 
21344                 for(var i = 0; i < rows.length; i++) {
21345                 
21346                     // how many rows should it span..
21347
21348                     var  cfg = {
21349                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21350                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21351
21352                         unselectable : "on",
21353                         cn : [
21354                             {
21355                                 cls: 'fc-event-inner',
21356                                 cn : [
21357     //                                {
21358     //                                  tag:'span',
21359     //                                  cls: 'fc-event-time',
21360     //                                  html : cells.length > 1 ? '' : ev.time
21361     //                                },
21362                                     {
21363                                       tag:'span',
21364                                       cls: 'fc-event-title',
21365                                       html : String.format('{0}', ev.title)
21366                                     }
21367
21368
21369                                 ]
21370                             },
21371                             {
21372                                 cls: 'ui-resizable-handle ui-resizable-e',
21373                                 html : '&nbsp;&nbsp;&nbsp'
21374                             }
21375
21376                         ]
21377                     };
21378
21379                     if (i == 0) {
21380                         cfg.cls += ' fc-event-start';
21381                     }
21382                     if ((i+1) == rows.length) {
21383                         cfg.cls += ' fc-event-end';
21384                     }
21385
21386                     var ctr = _this.el.select('.fc-event-container',true).first();
21387                     var cg = ctr.createChild(cfg);
21388
21389                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21390                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21391
21392                     var r = (c.more.length) ? 1 : 0;
21393                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21394                     cg.setWidth(ebox.right - sbox.x -2);
21395
21396                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21397                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21398                     cg.on('click', _this.onEventClick, _this, ev);
21399
21400                     ev.els.push(cg);
21401                     
21402                 }
21403                 
21404             }
21405             
21406             
21407             if(c.more.length){
21408                 var  cfg = {
21409                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21410                     style : 'position: absolute',
21411                     unselectable : "on",
21412                     cn : [
21413                         {
21414                             cls: 'fc-event-inner',
21415                             cn : [
21416                                 {
21417                                   tag:'span',
21418                                   cls: 'fc-event-title',
21419                                   html : 'More'
21420                                 }
21421
21422
21423                             ]
21424                         },
21425                         {
21426                             cls: 'ui-resizable-handle ui-resizable-e',
21427                             html : '&nbsp;&nbsp;&nbsp'
21428                         }
21429
21430                     ]
21431                 };
21432
21433                 var ctr = _this.el.select('.fc-event-container',true).first();
21434                 var cg = ctr.createChild(cfg);
21435
21436                 var sbox = c.select('.fc-day-content',true).first().getBox();
21437                 var ebox = c.select('.fc-day-content',true).first().getBox();
21438                 //Roo.log(cg);
21439                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21440                 cg.setWidth(ebox.right - sbox.x -2);
21441
21442                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21443                 
21444             }
21445             
21446         });
21447         
21448         
21449         
21450     },
21451     
21452     onEventEnter: function (e, el,event,d) {
21453         this.fireEvent('evententer', this, el, event);
21454     },
21455     
21456     onEventLeave: function (e, el,event,d) {
21457         this.fireEvent('eventleave', this, el, event);
21458     },
21459     
21460     onEventClick: function (e, el,event,d) {
21461         this.fireEvent('eventclick', this, el, event);
21462     },
21463     
21464     onMonthChange: function () {
21465         this.store.load();
21466     },
21467     
21468     onMoreEventClick: function(e, el, more)
21469     {
21470         var _this = this;
21471         
21472         this.calpopover.placement = 'right';
21473         this.calpopover.setTitle('More');
21474         
21475         this.calpopover.setContent('');
21476         
21477         var ctr = this.calpopover.el.select('.popover-content', true).first();
21478         
21479         Roo.each(more, function(m){
21480             var cfg = {
21481                 cls : 'fc-event-hori fc-event-draggable',
21482                 html : m.title
21483             };
21484             var cg = ctr.createChild(cfg);
21485             
21486             cg.on('click', _this.onEventClick, _this, m);
21487         });
21488         
21489         this.calpopover.show(el);
21490         
21491         
21492     },
21493     
21494     onLoad: function () 
21495     {   
21496         this.calevents = [];
21497         var cal = this;
21498         
21499         if(this.store.getCount() > 0){
21500             this.store.data.each(function(d){
21501                cal.addItem({
21502                     id : d.data.id,
21503                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21504                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21505                     time : d.data.start_time,
21506                     title : d.data.title,
21507                     description : d.data.description,
21508                     venue : d.data.venue
21509                 });
21510             });
21511         }
21512         
21513         this.renderEvents();
21514         
21515         if(this.calevents.length && this.loadMask){
21516             this.maskEl.hide();
21517         }
21518     },
21519     
21520     onBeforeLoad: function()
21521     {
21522         this.clearEvents();
21523         if(this.loadMask){
21524             this.maskEl.show();
21525         }
21526     }
21527 });
21528
21529  
21530  /*
21531  * - LGPL
21532  *
21533  * element
21534  * 
21535  */
21536
21537 /**
21538  * @class Roo.bootstrap.Popover
21539  * @extends Roo.bootstrap.Component
21540  * @parent none builder
21541  * @children Roo.bootstrap.Component
21542  * Bootstrap Popover class
21543  * @cfg {String} html contents of the popover   (or false to use children..)
21544  * @cfg {String} title of popover (or false to hide)
21545  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21546  * @cfg {String} trigger click || hover (or false to trigger manually)
21547  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21548  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21549  *      - if false and it has a 'parent' then it will be automatically added to that element
21550  *      - if string - Roo.get  will be called 
21551  * @cfg {Number} delay - delay before showing
21552  
21553  * @constructor
21554  * Create a new Popover
21555  * @param {Object} config The config object
21556  */
21557
21558 Roo.bootstrap.Popover = function(config){
21559     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21560     
21561     this.addEvents({
21562         // raw events
21563          /**
21564          * @event show
21565          * After the popover show
21566          * 
21567          * @param {Roo.bootstrap.Popover} this
21568          */
21569         "show" : true,
21570         /**
21571          * @event hide
21572          * After the popover hide
21573          * 
21574          * @param {Roo.bootstrap.Popover} this
21575          */
21576         "hide" : true
21577     });
21578 };
21579
21580 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21581     
21582     title: false,
21583     html: false,
21584     
21585     placement : 'right',
21586     trigger : 'hover', // hover
21587     modal : false,
21588     delay : 0,
21589     
21590     over: false,
21591     
21592     can_build_overlaid : false,
21593     
21594     maskEl : false, // the mask element
21595     headerEl : false,
21596     contentEl : false,
21597     alignEl : false, // when show is called with an element - this get's stored.
21598     
21599     getChildContainer : function()
21600     {
21601         return this.contentEl;
21602         
21603     },
21604     getPopoverHeader : function()
21605     {
21606         this.title = true; // flag not to hide it..
21607         this.headerEl.addClass('p-0');
21608         return this.headerEl
21609     },
21610     
21611     
21612     getAutoCreate : function(){
21613          
21614         var cfg = {
21615            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21616            style: 'display:block',
21617            cn : [
21618                 {
21619                     cls : 'arrow'
21620                 },
21621                 {
21622                     cls : 'popover-inner ',
21623                     cn : [
21624                         {
21625                             tag: 'h3',
21626                             cls: 'popover-title popover-header',
21627                             html : this.title === false ? '' : this.title
21628                         },
21629                         {
21630                             cls : 'popover-content popover-body '  + (this.cls || ''),
21631                             html : this.html || ''
21632                         }
21633                     ]
21634                     
21635                 }
21636            ]
21637         };
21638         
21639         return cfg;
21640     },
21641     /**
21642      * @param {string} the title
21643      */
21644     setTitle: function(str)
21645     {
21646         this.title = str;
21647         if (this.el) {
21648             this.headerEl.dom.innerHTML = str;
21649         }
21650         
21651     },
21652     /**
21653      * @param {string} the body content
21654      */
21655     setContent: function(str)
21656     {
21657         this.html = str;
21658         if (this.contentEl) {
21659             this.contentEl.dom.innerHTML = str;
21660         }
21661         
21662     },
21663     // as it get's added to the bottom of the page.
21664     onRender : function(ct, position)
21665     {
21666         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21667         
21668         
21669         
21670         if(!this.el){
21671             var cfg = Roo.apply({},  this.getAutoCreate());
21672             cfg.id = Roo.id();
21673             
21674             if (this.cls) {
21675                 cfg.cls += ' ' + this.cls;
21676             }
21677             if (this.style) {
21678                 cfg.style = this.style;
21679             }
21680             //Roo.log("adding to ");
21681             this.el = Roo.get(document.body).createChild(cfg, position);
21682 //            Roo.log(this.el);
21683         }
21684         
21685         this.contentEl = this.el.select('.popover-content',true).first();
21686         this.headerEl =  this.el.select('.popover-title',true).first();
21687         
21688         var nitems = [];
21689         if(typeof(this.items) != 'undefined'){
21690             var items = this.items;
21691             delete this.items;
21692
21693             for(var i =0;i < items.length;i++) {
21694                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21695             }
21696         }
21697
21698         this.items = nitems;
21699         
21700         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21701         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21702         
21703         
21704         
21705         this.initEvents();
21706     },
21707     
21708     resizeMask : function()
21709     {
21710         this.maskEl.setSize(
21711             Roo.lib.Dom.getViewWidth(true),
21712             Roo.lib.Dom.getViewHeight(true)
21713         );
21714     },
21715     
21716     initEvents : function()
21717     {
21718         
21719         if (!this.modal) { 
21720             Roo.bootstrap.Popover.register(this);
21721         }
21722          
21723         this.arrowEl = this.el.select('.arrow',true).first();
21724         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21725         this.el.enableDisplayMode('block');
21726         this.el.hide();
21727  
21728         
21729         if (this.over === false && !this.parent()) {
21730             return; 
21731         }
21732         if (this.triggers === false) {
21733             return;
21734         }
21735          
21736         // support parent
21737         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21738         var triggers = this.trigger ? this.trigger.split(' ') : [];
21739         Roo.each(triggers, function(trigger) {
21740         
21741             if (trigger == 'click') {
21742                 on_el.on('click', this.toggle, this);
21743             } else if (trigger != 'manual') {
21744                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21745                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21746       
21747                 on_el.on(eventIn  ,this.enter, this);
21748                 on_el.on(eventOut, this.leave, this);
21749             }
21750         }, this);
21751     },
21752     
21753     
21754     // private
21755     timeout : null,
21756     hoverState : null,
21757     
21758     toggle : function () {
21759         this.hoverState == 'in' ? this.leave() : this.enter();
21760     },
21761     
21762     enter : function () {
21763         
21764         clearTimeout(this.timeout);
21765     
21766         this.hoverState = 'in';
21767     
21768         if (!this.delay || !this.delay.show) {
21769             this.show();
21770             return;
21771         }
21772         var _t = this;
21773         this.timeout = setTimeout(function () {
21774             if (_t.hoverState == 'in') {
21775                 _t.show();
21776             }
21777         }, this.delay.show)
21778     },
21779     
21780     leave : function() {
21781         clearTimeout(this.timeout);
21782     
21783         this.hoverState = 'out';
21784     
21785         if (!this.delay || !this.delay.hide) {
21786             this.hide();
21787             return;
21788         }
21789         var _t = this;
21790         this.timeout = setTimeout(function () {
21791             if (_t.hoverState == 'out') {
21792                 _t.hide();
21793             }
21794         }, this.delay.hide)
21795     },
21796     
21797     /**
21798      * update the position of the dialog
21799      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21800      * 
21801      *
21802      */
21803     
21804     doAlign : function()
21805     {
21806         
21807         if (this.alignEl) {
21808             this.updatePosition(this.placement, true);
21809              
21810         } else {
21811             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21812             var es = this.el.getSize();
21813             var x = Roo.lib.Dom.getViewWidth()/2;
21814             var y = Roo.lib.Dom.getViewHeight()/2;
21815             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21816             
21817         }
21818
21819          
21820          
21821         
21822         
21823     },
21824     
21825     /**
21826      * Show the popover
21827      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21828      * @param {string} (left|right|top|bottom) position
21829      */
21830     show : function (on_el, placement)
21831     {
21832         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21833         on_el = on_el || false; // default to false
21834          
21835         if (!on_el) {
21836             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21837                 on_el = this.parent().el;
21838             } else if (this.over) {
21839                 on_el = Roo.get(this.over);
21840             }
21841             
21842         }
21843         
21844         this.alignEl = Roo.get( on_el );
21845
21846         if (!this.el) {
21847             this.render(document.body);
21848         }
21849         
21850         
21851          
21852         
21853         if (this.title === false) {
21854             this.headerEl.hide();
21855         }
21856         
21857        
21858         this.el.show();
21859         this.el.dom.style.display = 'block';
21860          
21861         this.doAlign();
21862         
21863         //var arrow = this.el.select('.arrow',true).first();
21864         //arrow.set(align[2], 
21865         
21866         this.el.addClass('in');
21867         
21868          
21869         
21870         this.hoverState = 'in';
21871         
21872         if (this.modal) {
21873             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21874             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21875             this.maskEl.dom.style.display = 'block';
21876             this.maskEl.addClass('show');
21877         }
21878         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21879  
21880         this.fireEvent('show', this);
21881         
21882     },
21883     /**
21884      * fire this manually after loading a grid in the table for example
21885      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21886      * @param {Boolean} try and move it if we cant get right position.
21887      */
21888     updatePosition : function(placement, try_move)
21889     {
21890         // allow for calling with no parameters
21891         placement = placement   ? placement :  this.placement;
21892         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21893         
21894         this.el.removeClass([
21895             'fade','top','bottom', 'left', 'right','in',
21896             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21897         ]);
21898         this.el.addClass(placement + ' bs-popover-' + placement);
21899         
21900         if (!this.alignEl ) {
21901             return false;
21902         }
21903         
21904         switch (placement) {
21905             case 'right':
21906                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21907                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21908                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21909                     //normal display... or moved up/down.
21910                     this.el.setXY(offset);
21911                     var xy = this.alignEl.getAnchorXY('tr', false);
21912                     xy[0]+=2;xy[1]+=5;
21913                     this.arrowEl.setXY(xy);
21914                     return true;
21915                 }
21916                 // continue through...
21917                 return this.updatePosition('left', false);
21918                 
21919             
21920             case 'left':
21921                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21922                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21923                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21924                     //normal display... or moved up/down.
21925                     this.el.setXY(offset);
21926                     var xy = this.alignEl.getAnchorXY('tl', false);
21927                     xy[0]-=10;xy[1]+=5; // << fix me
21928                     this.arrowEl.setXY(xy);
21929                     return true;
21930                 }
21931                 // call self...
21932                 return this.updatePosition('right', false);
21933             
21934             case 'top':
21935                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21936                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21937                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21938                     //normal display... or moved up/down.
21939                     this.el.setXY(offset);
21940                     var xy = this.alignEl.getAnchorXY('t', false);
21941                     xy[1]-=10; // << fix me
21942                     this.arrowEl.setXY(xy);
21943                     return true;
21944                 }
21945                 // fall through
21946                return this.updatePosition('bottom', false);
21947             
21948             case 'bottom':
21949                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21950                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21951                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21952                     //normal display... or moved up/down.
21953                     this.el.setXY(offset);
21954                     var xy = this.alignEl.getAnchorXY('b', false);
21955                      xy[1]+=2; // << fix me
21956                     this.arrowEl.setXY(xy);
21957                     return true;
21958                 }
21959                 // fall through
21960                 return this.updatePosition('top', false);
21961                 
21962             
21963         }
21964         
21965         
21966         return false;
21967     },
21968     
21969     hide : function()
21970     {
21971         this.el.setXY([0,0]);
21972         this.el.removeClass('in');
21973         this.el.hide();
21974         this.hoverState = null;
21975         this.maskEl.hide(); // always..
21976         this.fireEvent('hide', this);
21977     }
21978     
21979 });
21980
21981
21982 Roo.apply(Roo.bootstrap.Popover, {
21983
21984     alignment : {
21985         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21986         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21987         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21988         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21989     },
21990     
21991     zIndex : 20001,
21992
21993     clickHander : false,
21994     
21995     
21996
21997     onMouseDown : function(e)
21998     {
21999         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22000             /// what is nothing is showing..
22001             this.hideAll();
22002         }
22003          
22004     },
22005     
22006     
22007     popups : [],
22008     
22009     register : function(popup)
22010     {
22011         if (!Roo.bootstrap.Popover.clickHandler) {
22012             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22013         }
22014         // hide other popups.
22015         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22016         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22017         this.hideAll(); //<< why?
22018         //this.popups.push(popup);
22019     },
22020     hideAll : function()
22021     {
22022         this.popups.forEach(function(p) {
22023             p.hide();
22024         });
22025     },
22026     onShow : function() {
22027         Roo.bootstrap.Popover.popups.push(this);
22028     },
22029     onHide : function() {
22030         Roo.bootstrap.Popover.popups.remove(this);
22031     } 
22032
22033 });
22034 /**
22035  * @class Roo.bootstrap.PopoverNav
22036  * @extends Roo.bootstrap.nav.Simplebar
22037  * @parent Roo.bootstrap.Popover
22038  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22039  * @licence LGPL
22040  * Bootstrap Popover header navigation class
22041  * FIXME? should this go under nav?
22042  *
22043  * 
22044  * @constructor
22045  * Create a new Popover Header Navigation 
22046  * @param {Object} config The config object
22047  */
22048
22049 Roo.bootstrap.PopoverNav = function(config){
22050     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22051 };
22052
22053 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22054     
22055     
22056     container_method : 'getPopoverHeader' 
22057     
22058      
22059     
22060     
22061    
22062 });
22063
22064  
22065
22066  /*
22067  * - LGPL
22068  *
22069  * Progress
22070  * 
22071  */
22072
22073 /**
22074  * @class Roo.bootstrap.Progress
22075  * @extends Roo.bootstrap.Component
22076  * @children Roo.bootstrap.ProgressBar
22077  * Bootstrap Progress class
22078  * @cfg {Boolean} striped striped of the progress bar
22079  * @cfg {Boolean} active animated of the progress bar
22080  * 
22081  * 
22082  * @constructor
22083  * Create a new Progress
22084  * @param {Object} config The config object
22085  */
22086
22087 Roo.bootstrap.Progress = function(config){
22088     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22089 };
22090
22091 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22092     
22093     striped : false,
22094     active: false,
22095     
22096     getAutoCreate : function(){
22097         var cfg = {
22098             tag: 'div',
22099             cls: 'progress'
22100         };
22101         
22102         
22103         if(this.striped){
22104             cfg.cls += ' progress-striped';
22105         }
22106       
22107         if(this.active){
22108             cfg.cls += ' active';
22109         }
22110         
22111         
22112         return cfg;
22113     }
22114    
22115 });
22116
22117  
22118
22119  /*
22120  * - LGPL
22121  *
22122  * ProgressBar
22123  * 
22124  */
22125
22126 /**
22127  * @class Roo.bootstrap.ProgressBar
22128  * @extends Roo.bootstrap.Component
22129  * Bootstrap ProgressBar class
22130  * @cfg {Number} aria_valuenow aria-value now
22131  * @cfg {Number} aria_valuemin aria-value min
22132  * @cfg {Number} aria_valuemax aria-value max
22133  * @cfg {String} label label for the progress bar
22134  * @cfg {String} panel (success | info | warning | danger )
22135  * @cfg {String} role role of the progress bar
22136  * @cfg {String} sr_only text
22137  * 
22138  * 
22139  * @constructor
22140  * Create a new ProgressBar
22141  * @param {Object} config The config object
22142  */
22143
22144 Roo.bootstrap.ProgressBar = function(config){
22145     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22146 };
22147
22148 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22149     
22150     aria_valuenow : 0,
22151     aria_valuemin : 0,
22152     aria_valuemax : 100,
22153     label : false,
22154     panel : false,
22155     role : false,
22156     sr_only: false,
22157     
22158     getAutoCreate : function()
22159     {
22160         
22161         var cfg = {
22162             tag: 'div',
22163             cls: 'progress-bar',
22164             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22165         };
22166         
22167         if(this.sr_only){
22168             cfg.cn = {
22169                 tag: 'span',
22170                 cls: 'sr-only',
22171                 html: this.sr_only
22172             }
22173         }
22174         
22175         if(this.role){
22176             cfg.role = this.role;
22177         }
22178         
22179         if(this.aria_valuenow){
22180             cfg['aria-valuenow'] = this.aria_valuenow;
22181         }
22182         
22183         if(this.aria_valuemin){
22184             cfg['aria-valuemin'] = this.aria_valuemin;
22185         }
22186         
22187         if(this.aria_valuemax){
22188             cfg['aria-valuemax'] = this.aria_valuemax;
22189         }
22190         
22191         if(this.label && !this.sr_only){
22192             cfg.html = this.label;
22193         }
22194         
22195         if(this.panel){
22196             cfg.cls += ' progress-bar-' + this.panel;
22197         }
22198         
22199         return cfg;
22200     },
22201     
22202     update : function(aria_valuenow)
22203     {
22204         this.aria_valuenow = aria_valuenow;
22205         
22206         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22207     }
22208    
22209 });
22210
22211  
22212
22213  /**
22214  * @class Roo.bootstrap.TabGroup
22215  * @extends Roo.bootstrap.Column
22216  * @children Roo.bootstrap.TabPanel
22217  * Bootstrap Column class
22218  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22219  * @cfg {Boolean} carousel true to make the group behave like a carousel
22220  * @cfg {Boolean} bullets show bullets for the panels
22221  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22222  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22223  * @cfg {Boolean} showarrow (true|false) show arrow default true
22224  * 
22225  * @constructor
22226  * Create a new TabGroup
22227  * @param {Object} config The config object
22228  */
22229
22230 Roo.bootstrap.TabGroup = function(config){
22231     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22232     if (!this.navId) {
22233         this.navId = Roo.id();
22234     }
22235     this.tabs = [];
22236     Roo.bootstrap.TabGroup.register(this);
22237     
22238 };
22239
22240 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22241     
22242     carousel : false,
22243     transition : false,
22244     bullets : 0,
22245     timer : 0,
22246     autoslide : false,
22247     slideFn : false,
22248     slideOnTouch : false,
22249     showarrow : true,
22250     
22251     getAutoCreate : function()
22252     {
22253         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22254         
22255         cfg.cls += ' tab-content';
22256         
22257         if (this.carousel) {
22258             cfg.cls += ' carousel slide';
22259             
22260             cfg.cn = [{
22261                cls : 'carousel-inner',
22262                cn : []
22263             }];
22264         
22265             if(this.bullets  && !Roo.isTouch){
22266                 
22267                 var bullets = {
22268                     cls : 'carousel-bullets',
22269                     cn : []
22270                 };
22271                
22272                 if(this.bullets_cls){
22273                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22274                 }
22275                 
22276                 bullets.cn.push({
22277                     cls : 'clear'
22278                 });
22279                 
22280                 cfg.cn[0].cn.push(bullets);
22281             }
22282             
22283             if(this.showarrow){
22284                 cfg.cn[0].cn.push({
22285                     tag : 'div',
22286                     class : 'carousel-arrow',
22287                     cn : [
22288                         {
22289                             tag : 'div',
22290                             class : 'carousel-prev',
22291                             cn : [
22292                                 {
22293                                     tag : 'i',
22294                                     class : 'fa fa-chevron-left'
22295                                 }
22296                             ]
22297                         },
22298                         {
22299                             tag : 'div',
22300                             class : 'carousel-next',
22301                             cn : [
22302                                 {
22303                                     tag : 'i',
22304                                     class : 'fa fa-chevron-right'
22305                                 }
22306                             ]
22307                         }
22308                     ]
22309                 });
22310             }
22311             
22312         }
22313         
22314         return cfg;
22315     },
22316     
22317     initEvents:  function()
22318     {
22319 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22320 //            this.el.on("touchstart", this.onTouchStart, this);
22321 //        }
22322         
22323         if(this.autoslide){
22324             var _this = this;
22325             
22326             this.slideFn = window.setInterval(function() {
22327                 _this.showPanelNext();
22328             }, this.timer);
22329         }
22330         
22331         if(this.showarrow){
22332             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22333             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22334         }
22335         
22336         
22337     },
22338     
22339 //    onTouchStart : function(e, el, o)
22340 //    {
22341 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22342 //            return;
22343 //        }
22344 //        
22345 //        this.showPanelNext();
22346 //    },
22347     
22348     
22349     getChildContainer : function()
22350     {
22351         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22352     },
22353     
22354     /**
22355     * register a Navigation item
22356     * @param {Roo.bootstrap.nav.Item} the navitem to add
22357     */
22358     register : function(item)
22359     {
22360         this.tabs.push( item);
22361         item.navId = this.navId; // not really needed..
22362         this.addBullet();
22363     
22364     },
22365     
22366     getActivePanel : function()
22367     {
22368         var r = false;
22369         Roo.each(this.tabs, function(t) {
22370             if (t.active) {
22371                 r = t;
22372                 return false;
22373             }
22374             return null;
22375         });
22376         return r;
22377         
22378     },
22379     getPanelByName : function(n)
22380     {
22381         var r = false;
22382         Roo.each(this.tabs, function(t) {
22383             if (t.tabId == n) {
22384                 r = t;
22385                 return false;
22386             }
22387             return null;
22388         });
22389         return r;
22390     },
22391     indexOfPanel : function(p)
22392     {
22393         var r = false;
22394         Roo.each(this.tabs, function(t,i) {
22395             if (t.tabId == p.tabId) {
22396                 r = i;
22397                 return false;
22398             }
22399             return null;
22400         });
22401         return r;
22402     },
22403     /**
22404      * show a specific panel
22405      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22406      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22407      */
22408     showPanel : function (pan)
22409     {
22410         if(this.transition || typeof(pan) == 'undefined'){
22411             Roo.log("waiting for the transitionend");
22412             return false;
22413         }
22414         
22415         if (typeof(pan) == 'number') {
22416             pan = this.tabs[pan];
22417         }
22418         
22419         if (typeof(pan) == 'string') {
22420             pan = this.getPanelByName(pan);
22421         }
22422         
22423         var cur = this.getActivePanel();
22424         
22425         if(!pan || !cur){
22426             Roo.log('pan or acitve pan is undefined');
22427             return false;
22428         }
22429         
22430         if (pan.tabId == this.getActivePanel().tabId) {
22431             return true;
22432         }
22433         
22434         if (false === cur.fireEvent('beforedeactivate')) {
22435             return false;
22436         }
22437         
22438         if(this.bullets > 0 && !Roo.isTouch){
22439             this.setActiveBullet(this.indexOfPanel(pan));
22440         }
22441         
22442         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22443             
22444             //class="carousel-item carousel-item-next carousel-item-left"
22445             
22446             this.transition = true;
22447             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22448             var lr = dir == 'next' ? 'left' : 'right';
22449             pan.el.addClass(dir); // or prev
22450             pan.el.addClass('carousel-item-' + dir); // or prev
22451             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22452             cur.el.addClass(lr); // or right
22453             pan.el.addClass(lr);
22454             cur.el.addClass('carousel-item-' +lr); // or right
22455             pan.el.addClass('carousel-item-' +lr);
22456             
22457             
22458             var _this = this;
22459             cur.el.on('transitionend', function() {
22460                 Roo.log("trans end?");
22461                 
22462                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22463                 pan.setActive(true);
22464                 
22465                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22466                 cur.setActive(false);
22467                 
22468                 _this.transition = false;
22469                 
22470             }, this, { single:  true } );
22471             
22472             return true;
22473         }
22474         
22475         cur.setActive(false);
22476         pan.setActive(true);
22477         
22478         return true;
22479         
22480     },
22481     showPanelNext : function()
22482     {
22483         var i = this.indexOfPanel(this.getActivePanel());
22484         
22485         if (i >= this.tabs.length - 1 && !this.autoslide) {
22486             return;
22487         }
22488         
22489         if (i >= this.tabs.length - 1 && this.autoslide) {
22490             i = -1;
22491         }
22492         
22493         this.showPanel(this.tabs[i+1]);
22494     },
22495     
22496     showPanelPrev : function()
22497     {
22498         var i = this.indexOfPanel(this.getActivePanel());
22499         
22500         if (i  < 1 && !this.autoslide) {
22501             return;
22502         }
22503         
22504         if (i < 1 && this.autoslide) {
22505             i = this.tabs.length;
22506         }
22507         
22508         this.showPanel(this.tabs[i-1]);
22509     },
22510     
22511     
22512     addBullet: function()
22513     {
22514         if(!this.bullets || Roo.isTouch){
22515             return;
22516         }
22517         var ctr = this.el.select('.carousel-bullets',true).first();
22518         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22519         var bullet = ctr.createChild({
22520             cls : 'bullet bullet-' + i
22521         },ctr.dom.lastChild);
22522         
22523         
22524         var _this = this;
22525         
22526         bullet.on('click', (function(e, el, o, ii, t){
22527
22528             e.preventDefault();
22529
22530             this.showPanel(ii);
22531
22532             if(this.autoslide && this.slideFn){
22533                 clearInterval(this.slideFn);
22534                 this.slideFn = window.setInterval(function() {
22535                     _this.showPanelNext();
22536                 }, this.timer);
22537             }
22538
22539         }).createDelegate(this, [i, bullet], true));
22540                 
22541         
22542     },
22543      
22544     setActiveBullet : function(i)
22545     {
22546         if(Roo.isTouch){
22547             return;
22548         }
22549         
22550         Roo.each(this.el.select('.bullet', true).elements, function(el){
22551             el.removeClass('selected');
22552         });
22553
22554         var bullet = this.el.select('.bullet-' + i, true).first();
22555         
22556         if(!bullet){
22557             return;
22558         }
22559         
22560         bullet.addClass('selected');
22561     }
22562     
22563     
22564   
22565 });
22566
22567  
22568
22569  
22570  
22571 Roo.apply(Roo.bootstrap.TabGroup, {
22572     
22573     groups: {},
22574      /**
22575     * register a Navigation Group
22576     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22577     */
22578     register : function(navgrp)
22579     {
22580         this.groups[navgrp.navId] = navgrp;
22581         
22582     },
22583     /**
22584     * fetch a Navigation Group based on the navigation ID
22585     * if one does not exist , it will get created.
22586     * @param {string} the navgroup to add
22587     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22588     */
22589     get: function(navId) {
22590         if (typeof(this.groups[navId]) == 'undefined') {
22591             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22592         }
22593         return this.groups[navId] ;
22594     }
22595     
22596     
22597     
22598 });
22599
22600  /*
22601  * - LGPL
22602  *
22603  * TabPanel
22604  * 
22605  */
22606
22607 /**
22608  * @class Roo.bootstrap.TabPanel
22609  * @extends Roo.bootstrap.Component
22610  * @children Roo.bootstrap.Component
22611  * Bootstrap TabPanel class
22612  * @cfg {Boolean} active panel active
22613  * @cfg {String} html panel content
22614  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22615  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22616  * @cfg {String} href click to link..
22617  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22618  * 
22619  * 
22620  * @constructor
22621  * Create a new TabPanel
22622  * @param {Object} config The config object
22623  */
22624
22625 Roo.bootstrap.TabPanel = function(config){
22626     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22627     this.addEvents({
22628         /**
22629              * @event changed
22630              * Fires when the active status changes
22631              * @param {Roo.bootstrap.TabPanel} this
22632              * @param {Boolean} state the new state
22633             
22634          */
22635         'changed': true,
22636         /**
22637              * @event beforedeactivate
22638              * Fires before a tab is de-activated - can be used to do validation on a form.
22639              * @param {Roo.bootstrap.TabPanel} this
22640              * @return {Boolean} false if there is an error
22641             
22642          */
22643         'beforedeactivate': true
22644      });
22645     
22646     this.tabId = this.tabId || Roo.id();
22647   
22648 };
22649
22650 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22651     
22652     active: false,
22653     html: false,
22654     tabId: false,
22655     navId : false,
22656     href : '',
22657     touchSlide : false,
22658     getAutoCreate : function(){
22659         
22660         
22661         var cfg = {
22662             tag: 'div',
22663             // item is needed for carousel - not sure if it has any effect otherwise
22664             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22665             html: this.html || ''
22666         };
22667         
22668         if(this.active){
22669             cfg.cls += ' active';
22670         }
22671         
22672         if(this.tabId){
22673             cfg.tabId = this.tabId;
22674         }
22675         
22676         
22677         
22678         return cfg;
22679     },
22680     
22681     initEvents:  function()
22682     {
22683         var p = this.parent();
22684         
22685         this.navId = this.navId || p.navId;
22686         
22687         if (typeof(this.navId) != 'undefined') {
22688             // not really needed.. but just in case.. parent should be a NavGroup.
22689             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22690             
22691             tg.register(this);
22692             
22693             var i = tg.tabs.length - 1;
22694             
22695             if(this.active && tg.bullets > 0 && i < tg.bullets){
22696                 tg.setActiveBullet(i);
22697             }
22698         }
22699         
22700         this.el.on('click', this.onClick, this);
22701         
22702         if(Roo.isTouch && this.touchSlide){
22703             this.el.on("touchstart", this.onTouchStart, this);
22704             this.el.on("touchmove", this.onTouchMove, this);
22705             this.el.on("touchend", this.onTouchEnd, this);
22706         }
22707         
22708     },
22709     
22710     onRender : function(ct, position)
22711     {
22712         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22713     },
22714     
22715     setActive : function(state)
22716     {
22717         Roo.log("panel - set active " + this.tabId + "=" + state);
22718         
22719         this.active = state;
22720         if (!state) {
22721             this.el.removeClass('active');
22722             
22723         } else  if (!this.el.hasClass('active')) {
22724             this.el.addClass('active');
22725         }
22726         
22727         this.fireEvent('changed', this, state);
22728     },
22729     
22730     onClick : function(e)
22731     {
22732         e.preventDefault();
22733         
22734         if(!this.href.length){
22735             return;
22736         }
22737         
22738         window.location.href = this.href;
22739     },
22740     
22741     startX : 0,
22742     startY : 0,
22743     endX : 0,
22744     endY : 0,
22745     swiping : false,
22746     
22747     onTouchStart : function(e)
22748     {
22749         this.swiping = false;
22750         
22751         this.startX = e.browserEvent.touches[0].clientX;
22752         this.startY = e.browserEvent.touches[0].clientY;
22753     },
22754     
22755     onTouchMove : function(e)
22756     {
22757         this.swiping = true;
22758         
22759         this.endX = e.browserEvent.touches[0].clientX;
22760         this.endY = e.browserEvent.touches[0].clientY;
22761     },
22762     
22763     onTouchEnd : function(e)
22764     {
22765         if(!this.swiping){
22766             this.onClick(e);
22767             return;
22768         }
22769         
22770         var tabGroup = this.parent();
22771         
22772         if(this.endX > this.startX){ // swiping right
22773             tabGroup.showPanelPrev();
22774             return;
22775         }
22776         
22777         if(this.startX > this.endX){ // swiping left
22778             tabGroup.showPanelNext();
22779             return;
22780         }
22781     }
22782     
22783     
22784 });
22785  
22786
22787  
22788
22789  /*
22790  * - LGPL
22791  *
22792  * DateField
22793  * 
22794  */
22795
22796 /**
22797  * @class Roo.bootstrap.form.DateField
22798  * @extends Roo.bootstrap.form.Input
22799  * Bootstrap DateField class
22800  * @cfg {Number} weekStart default 0
22801  * @cfg {String} viewMode default empty, (months|years)
22802  * @cfg {String} minViewMode default empty, (months|years)
22803  * @cfg {Number} startDate default -Infinity
22804  * @cfg {Number} endDate default Infinity
22805  * @cfg {Boolean} todayHighlight default false
22806  * @cfg {Boolean} todayBtn default false
22807  * @cfg {Boolean} calendarWeeks default false
22808  * @cfg {Object} daysOfWeekDisabled default empty
22809  * @cfg {Boolean} singleMode default false (true | false)
22810  * 
22811  * @cfg {Boolean} keyboardNavigation default true
22812  * @cfg {String} language default en
22813  * 
22814  * @constructor
22815  * Create a new DateField
22816  * @param {Object} config The config object
22817  */
22818
22819 Roo.bootstrap.form.DateField = function(config){
22820     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22821      this.addEvents({
22822             /**
22823              * @event show
22824              * Fires when this field show.
22825              * @param {Roo.bootstrap.form.DateField} this
22826              * @param {Mixed} date The date value
22827              */
22828             show : true,
22829             /**
22830              * @event show
22831              * Fires when this field hide.
22832              * @param {Roo.bootstrap.form.DateField} this
22833              * @param {Mixed} date The date value
22834              */
22835             hide : true,
22836             /**
22837              * @event select
22838              * Fires when select a date.
22839              * @param {Roo.bootstrap.form.DateField} this
22840              * @param {Mixed} date The date value
22841              */
22842             select : true,
22843             /**
22844              * @event beforeselect
22845              * Fires when before select a date.
22846              * @param {Roo.bootstrap.form.DateField} this
22847              * @param {Mixed} date The date value
22848              */
22849             beforeselect : true
22850         });
22851 };
22852
22853 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22854     
22855     /**
22856      * @cfg {String} format
22857      * The default date format string which can be overriden for localization support.  The format must be
22858      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22859      */
22860     format : "m/d/y",
22861     /**
22862      * @cfg {String} altFormats
22863      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22864      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22865      */
22866     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22867     
22868     weekStart : 0,
22869     
22870     viewMode : '',
22871     
22872     minViewMode : '',
22873     
22874     todayHighlight : false,
22875     
22876     todayBtn: false,
22877     
22878     language: 'en',
22879     
22880     keyboardNavigation: true,
22881     
22882     calendarWeeks: false,
22883     
22884     startDate: -Infinity,
22885     
22886     endDate: Infinity,
22887     
22888     daysOfWeekDisabled: [],
22889     
22890     _events: [],
22891     
22892     singleMode : false,
22893     
22894     UTCDate: function()
22895     {
22896         return new Date(Date.UTC.apply(Date, arguments));
22897     },
22898     
22899     UTCToday: function()
22900     {
22901         var today = new Date();
22902         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22903     },
22904     
22905     getDate: function() {
22906             var d = this.getUTCDate();
22907             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22908     },
22909     
22910     getUTCDate: function() {
22911             return this.date;
22912     },
22913     
22914     setDate: function(d) {
22915             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22916     },
22917     
22918     setUTCDate: function(d) {
22919             this.date = d;
22920             this.setValue(this.formatDate(this.date));
22921     },
22922         
22923     onRender: function(ct, position)
22924     {
22925         
22926         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22927         
22928         this.language = this.language || 'en';
22929         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22930         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22931         
22932         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22933         this.format = this.format || 'm/d/y';
22934         this.isInline = false;
22935         this.isInput = true;
22936         this.component = this.el.select('.add-on', true).first() || false;
22937         this.component = (this.component && this.component.length === 0) ? false : this.component;
22938         this.hasInput = this.component && this.inputEl().length;
22939         
22940         if (typeof(this.minViewMode === 'string')) {
22941             switch (this.minViewMode) {
22942                 case 'months':
22943                     this.minViewMode = 1;
22944                     break;
22945                 case 'years':
22946                     this.minViewMode = 2;
22947                     break;
22948                 default:
22949                     this.minViewMode = 0;
22950                     break;
22951             }
22952         }
22953         
22954         if (typeof(this.viewMode === 'string')) {
22955             switch (this.viewMode) {
22956                 case 'months':
22957                     this.viewMode = 1;
22958                     break;
22959                 case 'years':
22960                     this.viewMode = 2;
22961                     break;
22962                 default:
22963                     this.viewMode = 0;
22964                     break;
22965             }
22966         }
22967                 
22968         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22969         
22970 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22971         
22972         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22973         
22974         this.picker().on('mousedown', this.onMousedown, this);
22975         this.picker().on('click', this.onClick, this);
22976         
22977         this.picker().addClass('datepicker-dropdown');
22978         
22979         this.startViewMode = this.viewMode;
22980         
22981         if(this.singleMode){
22982             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22983                 v.setVisibilityMode(Roo.Element.DISPLAY);
22984                 v.hide();
22985             });
22986             
22987             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22988                 v.setStyle('width', '189px');
22989             });
22990         }
22991         
22992         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22993             if(!this.calendarWeeks){
22994                 v.remove();
22995                 return;
22996             }
22997             
22998             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22999             v.attr('colspan', function(i, val){
23000                 return parseInt(val) + 1;
23001             });
23002         });
23003                         
23004         
23005         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23006         
23007         this.setStartDate(this.startDate);
23008         this.setEndDate(this.endDate);
23009         
23010         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23011         
23012         this.fillDow();
23013         this.fillMonths();
23014         this.update();
23015         this.showMode();
23016         
23017         if(this.isInline) {
23018             this.showPopup();
23019         }
23020     },
23021     
23022     picker : function()
23023     {
23024         return this.pickerEl;
23025 //        return this.el.select('.datepicker', true).first();
23026     },
23027     
23028     fillDow: function()
23029     {
23030         var dowCnt = this.weekStart;
23031         
23032         var dow = {
23033             tag: 'tr',
23034             cn: [
23035                 
23036             ]
23037         };
23038         
23039         if(this.calendarWeeks){
23040             dow.cn.push({
23041                 tag: 'th',
23042                 cls: 'cw',
23043                 html: '&nbsp;'
23044             })
23045         }
23046         
23047         while (dowCnt < this.weekStart + 7) {
23048             dow.cn.push({
23049                 tag: 'th',
23050                 cls: 'dow',
23051                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23052             });
23053         }
23054         
23055         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23056     },
23057     
23058     fillMonths: function()
23059     {    
23060         var i = 0;
23061         var months = this.picker().select('>.datepicker-months td', true).first();
23062         
23063         months.dom.innerHTML = '';
23064         
23065         while (i < 12) {
23066             var month = {
23067                 tag: 'span',
23068                 cls: 'month',
23069                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23070             };
23071             
23072             months.createChild(month);
23073         }
23074         
23075     },
23076     
23077     update: function()
23078     {
23079         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;
23080         
23081         if (this.date < this.startDate) {
23082             this.viewDate = new Date(this.startDate);
23083         } else if (this.date > this.endDate) {
23084             this.viewDate = new Date(this.endDate);
23085         } else {
23086             this.viewDate = new Date(this.date);
23087         }
23088         
23089         this.fill();
23090     },
23091     
23092     fill: function() 
23093     {
23094         var d = new Date(this.viewDate),
23095                 year = d.getUTCFullYear(),
23096                 month = d.getUTCMonth(),
23097                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23098                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23099                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23100                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23101                 currentDate = this.date && this.date.valueOf(),
23102                 today = this.UTCToday();
23103         
23104         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23105         
23106 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23107         
23108 //        this.picker.select('>tfoot th.today').
23109 //                                              .text(dates[this.language].today)
23110 //                                              .toggle(this.todayBtn !== false);
23111     
23112         this.updateNavArrows();
23113         this.fillMonths();
23114                                                 
23115         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23116         
23117         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23118          
23119         prevMonth.setUTCDate(day);
23120         
23121         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23122         
23123         var nextMonth = new Date(prevMonth);
23124         
23125         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23126         
23127         nextMonth = nextMonth.valueOf();
23128         
23129         var fillMonths = false;
23130         
23131         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23132         
23133         while(prevMonth.valueOf() <= nextMonth) {
23134             var clsName = '';
23135             
23136             if (prevMonth.getUTCDay() === this.weekStart) {
23137                 if(fillMonths){
23138                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23139                 }
23140                     
23141                 fillMonths = {
23142                     tag: 'tr',
23143                     cn: []
23144                 };
23145                 
23146                 if(this.calendarWeeks){
23147                     // ISO 8601: First week contains first thursday.
23148                     // ISO also states week starts on Monday, but we can be more abstract here.
23149                     var
23150                     // Start of current week: based on weekstart/current date
23151                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23152                     // Thursday of this week
23153                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23154                     // First Thursday of year, year from thursday
23155                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23156                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23157                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23158                     
23159                     fillMonths.cn.push({
23160                         tag: 'td',
23161                         cls: 'cw',
23162                         html: calWeek
23163                     });
23164                 }
23165             }
23166             
23167             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23168                 clsName += ' old';
23169             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23170                 clsName += ' new';
23171             }
23172             if (this.todayHighlight &&
23173                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23174                 prevMonth.getUTCMonth() == today.getMonth() &&
23175                 prevMonth.getUTCDate() == today.getDate()) {
23176                 clsName += ' today';
23177             }
23178             
23179             if (currentDate && prevMonth.valueOf() === currentDate) {
23180                 clsName += ' active';
23181             }
23182             
23183             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23184                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23185                     clsName += ' disabled';
23186             }
23187             
23188             fillMonths.cn.push({
23189                 tag: 'td',
23190                 cls: 'day ' + clsName,
23191                 html: prevMonth.getDate()
23192             });
23193             
23194             prevMonth.setDate(prevMonth.getDate()+1);
23195         }
23196           
23197         var currentYear = this.date && this.date.getUTCFullYear();
23198         var currentMonth = this.date && this.date.getUTCMonth();
23199         
23200         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23201         
23202         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23203             v.removeClass('active');
23204             
23205             if(currentYear === year && k === currentMonth){
23206                 v.addClass('active');
23207             }
23208             
23209             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23210                 v.addClass('disabled');
23211             }
23212             
23213         });
23214         
23215         
23216         year = parseInt(year/10, 10) * 10;
23217         
23218         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23219         
23220         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23221         
23222         year -= 1;
23223         for (var i = -1; i < 11; i++) {
23224             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23225                 tag: 'span',
23226                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23227                 html: year
23228             });
23229             
23230             year += 1;
23231         }
23232     },
23233     
23234     showMode: function(dir) 
23235     {
23236         if (dir) {
23237             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23238         }
23239         
23240         Roo.each(this.picker().select('>div',true).elements, function(v){
23241             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23242             v.hide();
23243         });
23244         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23245     },
23246     
23247     place: function()
23248     {
23249         if(this.isInline) {
23250             return;
23251         }
23252         
23253         this.picker().removeClass(['bottom', 'top']);
23254         
23255         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23256             /*
23257              * place to the top of element!
23258              *
23259              */
23260             
23261             this.picker().addClass('top');
23262             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23263             
23264             return;
23265         }
23266         
23267         this.picker().addClass('bottom');
23268         
23269         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23270     },
23271     
23272     parseDate : function(value)
23273     {
23274         if(!value || value instanceof Date){
23275             return value;
23276         }
23277         var v = Date.parseDate(value, this.format);
23278         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23279             v = Date.parseDate(value, 'Y-m-d');
23280         }
23281         if(!v && this.altFormats){
23282             if(!this.altFormatsArray){
23283                 this.altFormatsArray = this.altFormats.split("|");
23284             }
23285             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23286                 v = Date.parseDate(value, this.altFormatsArray[i]);
23287             }
23288         }
23289         return v;
23290     },
23291     
23292     formatDate : function(date, fmt)
23293     {   
23294         return (!date || !(date instanceof Date)) ?
23295         date : date.dateFormat(fmt || this.format);
23296     },
23297     
23298     onFocus : function()
23299     {
23300         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23301         this.showPopup();
23302     },
23303     
23304     onBlur : function()
23305     {
23306         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23307         
23308         var d = this.inputEl().getValue();
23309         
23310         this.setValue(d);
23311                 
23312         this.hidePopup();
23313     },
23314     
23315     showPopup : function()
23316     {
23317         this.picker().show();
23318         this.update();
23319         this.place();
23320         
23321         this.fireEvent('showpopup', this, this.date);
23322     },
23323     
23324     hidePopup : function()
23325     {
23326         if(this.isInline) {
23327             return;
23328         }
23329         this.picker().hide();
23330         this.viewMode = this.startViewMode;
23331         this.showMode();
23332         
23333         this.fireEvent('hidepopup', this, this.date);
23334         
23335     },
23336     
23337     onMousedown: function(e)
23338     {
23339         e.stopPropagation();
23340         e.preventDefault();
23341     },
23342     
23343     keyup: function(e)
23344     {
23345         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23346         this.update();
23347     },
23348
23349     setValue: function(v)
23350     {
23351         if(this.fireEvent('beforeselect', this, v) !== false){
23352             var d = new Date(this.parseDate(v) ).clearTime();
23353         
23354             if(isNaN(d.getTime())){
23355                 this.date = this.viewDate = '';
23356                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23357                 return;
23358             }
23359
23360             v = this.formatDate(d);
23361
23362             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23363
23364             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23365
23366             this.update();
23367
23368             this.fireEvent('select', this, this.date);
23369         }
23370     },
23371     
23372     getValue: function()
23373     {
23374         return this.formatDate(this.date);
23375     },
23376     
23377     fireKey: function(e)
23378     {
23379         if (!this.picker().isVisible()){
23380             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23381                 this.showPopup();
23382             }
23383             return;
23384         }
23385         
23386         var dateChanged = false,
23387         dir, day, month,
23388         newDate, newViewDate;
23389         
23390         switch(e.keyCode){
23391             case 27: // escape
23392                 this.hidePopup();
23393                 e.preventDefault();
23394                 break;
23395             case 37: // left
23396             case 39: // right
23397                 if (!this.keyboardNavigation) {
23398                     break;
23399                 }
23400                 dir = e.keyCode == 37 ? -1 : 1;
23401                 
23402                 if (e.ctrlKey){
23403                     newDate = this.moveYear(this.date, dir);
23404                     newViewDate = this.moveYear(this.viewDate, dir);
23405                 } else if (e.shiftKey){
23406                     newDate = this.moveMonth(this.date, dir);
23407                     newViewDate = this.moveMonth(this.viewDate, dir);
23408                 } else {
23409                     newDate = new Date(this.date);
23410                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23411                     newViewDate = new Date(this.viewDate);
23412                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23413                 }
23414                 if (this.dateWithinRange(newDate)){
23415                     this.date = newDate;
23416                     this.viewDate = newViewDate;
23417                     this.setValue(this.formatDate(this.date));
23418 //                    this.update();
23419                     e.preventDefault();
23420                     dateChanged = true;
23421                 }
23422                 break;
23423             case 38: // up
23424             case 40: // down
23425                 if (!this.keyboardNavigation) {
23426                     break;
23427                 }
23428                 dir = e.keyCode == 38 ? -1 : 1;
23429                 if (e.ctrlKey){
23430                     newDate = this.moveYear(this.date, dir);
23431                     newViewDate = this.moveYear(this.viewDate, dir);
23432                 } else if (e.shiftKey){
23433                     newDate = this.moveMonth(this.date, dir);
23434                     newViewDate = this.moveMonth(this.viewDate, dir);
23435                 } else {
23436                     newDate = new Date(this.date);
23437                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23438                     newViewDate = new Date(this.viewDate);
23439                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23440                 }
23441                 if (this.dateWithinRange(newDate)){
23442                     this.date = newDate;
23443                     this.viewDate = newViewDate;
23444                     this.setValue(this.formatDate(this.date));
23445 //                    this.update();
23446                     e.preventDefault();
23447                     dateChanged = true;
23448                 }
23449                 break;
23450             case 13: // enter
23451                 this.setValue(this.formatDate(this.date));
23452                 this.hidePopup();
23453                 e.preventDefault();
23454                 break;
23455             case 9: // tab
23456                 this.setValue(this.formatDate(this.date));
23457                 this.hidePopup();
23458                 break;
23459             case 16: // shift
23460             case 17: // ctrl
23461             case 18: // alt
23462                 break;
23463             default :
23464                 this.hidePopup();
23465                 
23466         }
23467     },
23468     
23469     
23470     onClick: function(e) 
23471     {
23472         e.stopPropagation();
23473         e.preventDefault();
23474         
23475         var target = e.getTarget();
23476         
23477         if(target.nodeName.toLowerCase() === 'i'){
23478             target = Roo.get(target).dom.parentNode;
23479         }
23480         
23481         var nodeName = target.nodeName;
23482         var className = target.className;
23483         var html = target.innerHTML;
23484         //Roo.log(nodeName);
23485         
23486         switch(nodeName.toLowerCase()) {
23487             case 'th':
23488                 switch(className) {
23489                     case 'switch':
23490                         this.showMode(1);
23491                         break;
23492                     case 'prev':
23493                     case 'next':
23494                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23495                         switch(this.viewMode){
23496                                 case 0:
23497                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23498                                         break;
23499                                 case 1:
23500                                 case 2:
23501                                         this.viewDate = this.moveYear(this.viewDate, dir);
23502                                         break;
23503                         }
23504                         this.fill();
23505                         break;
23506                     case 'today':
23507                         var date = new Date();
23508                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23509 //                        this.fill()
23510                         this.setValue(this.formatDate(this.date));
23511                         
23512                         this.hidePopup();
23513                         break;
23514                 }
23515                 break;
23516             case 'span':
23517                 if (className.indexOf('disabled') < 0) {
23518                 if (!this.viewDate) {
23519                     this.viewDate = new Date();
23520                 }
23521                 this.viewDate.setUTCDate(1);
23522                     if (className.indexOf('month') > -1) {
23523                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23524                     } else {
23525                         var year = parseInt(html, 10) || 0;
23526                         this.viewDate.setUTCFullYear(year);
23527                         
23528                     }
23529                     
23530                     if(this.singleMode){
23531                         this.setValue(this.formatDate(this.viewDate));
23532                         this.hidePopup();
23533                         return;
23534                     }
23535                     
23536                     this.showMode(-1);
23537                     this.fill();
23538                 }
23539                 break;
23540                 
23541             case 'td':
23542                 //Roo.log(className);
23543                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23544                     var day = parseInt(html, 10) || 1;
23545                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23546                         month = (this.viewDate || new Date()).getUTCMonth();
23547
23548                     if (className.indexOf('old') > -1) {
23549                         if(month === 0 ){
23550                             month = 11;
23551                             year -= 1;
23552                         }else{
23553                             month -= 1;
23554                         }
23555                     } else if (className.indexOf('new') > -1) {
23556                         if (month == 11) {
23557                             month = 0;
23558                             year += 1;
23559                         } else {
23560                             month += 1;
23561                         }
23562                     }
23563                     //Roo.log([year,month,day]);
23564                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23565                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23566 //                    this.fill();
23567                     //Roo.log(this.formatDate(this.date));
23568                     this.setValue(this.formatDate(this.date));
23569                     this.hidePopup();
23570                 }
23571                 break;
23572         }
23573     },
23574     
23575     setStartDate: function(startDate)
23576     {
23577         this.startDate = startDate || -Infinity;
23578         if (this.startDate !== -Infinity) {
23579             this.startDate = this.parseDate(this.startDate);
23580         }
23581         this.update();
23582         this.updateNavArrows();
23583     },
23584
23585     setEndDate: function(endDate)
23586     {
23587         this.endDate = endDate || Infinity;
23588         if (this.endDate !== Infinity) {
23589             this.endDate = this.parseDate(this.endDate);
23590         }
23591         this.update();
23592         this.updateNavArrows();
23593     },
23594     
23595     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23596     {
23597         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23598         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23599             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23600         }
23601         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23602             return parseInt(d, 10);
23603         });
23604         this.update();
23605         this.updateNavArrows();
23606     },
23607     
23608     updateNavArrows: function() 
23609     {
23610         if(this.singleMode){
23611             return;
23612         }
23613         
23614         var d = new Date(this.viewDate),
23615         year = d.getUTCFullYear(),
23616         month = d.getUTCMonth();
23617         
23618         Roo.each(this.picker().select('.prev', true).elements, function(v){
23619             v.show();
23620             switch (this.viewMode) {
23621                 case 0:
23622
23623                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23624                         v.hide();
23625                     }
23626                     break;
23627                 case 1:
23628                 case 2:
23629                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23630                         v.hide();
23631                     }
23632                     break;
23633             }
23634         });
23635         
23636         Roo.each(this.picker().select('.next', true).elements, function(v){
23637             v.show();
23638             switch (this.viewMode) {
23639                 case 0:
23640
23641                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23642                         v.hide();
23643                     }
23644                     break;
23645                 case 1:
23646                 case 2:
23647                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23648                         v.hide();
23649                     }
23650                     break;
23651             }
23652         })
23653     },
23654     
23655     moveMonth: function(date, dir)
23656     {
23657         if (!dir) {
23658             return date;
23659         }
23660         var new_date = new Date(date.valueOf()),
23661         day = new_date.getUTCDate(),
23662         month = new_date.getUTCMonth(),
23663         mag = Math.abs(dir),
23664         new_month, test;
23665         dir = dir > 0 ? 1 : -1;
23666         if (mag == 1){
23667             test = dir == -1
23668             // If going back one month, make sure month is not current month
23669             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23670             ? function(){
23671                 return new_date.getUTCMonth() == month;
23672             }
23673             // If going forward one month, make sure month is as expected
23674             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23675             : function(){
23676                 return new_date.getUTCMonth() != new_month;
23677             };
23678             new_month = month + dir;
23679             new_date.setUTCMonth(new_month);
23680             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23681             if (new_month < 0 || new_month > 11) {
23682                 new_month = (new_month + 12) % 12;
23683             }
23684         } else {
23685             // For magnitudes >1, move one month at a time...
23686             for (var i=0; i<mag; i++) {
23687                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23688                 new_date = this.moveMonth(new_date, dir);
23689             }
23690             // ...then reset the day, keeping it in the new month
23691             new_month = new_date.getUTCMonth();
23692             new_date.setUTCDate(day);
23693             test = function(){
23694                 return new_month != new_date.getUTCMonth();
23695             };
23696         }
23697         // Common date-resetting loop -- if date is beyond end of month, make it
23698         // end of month
23699         while (test()){
23700             new_date.setUTCDate(--day);
23701             new_date.setUTCMonth(new_month);
23702         }
23703         return new_date;
23704     },
23705
23706     moveYear: function(date, dir)
23707     {
23708         return this.moveMonth(date, dir*12);
23709     },
23710
23711     dateWithinRange: function(date)
23712     {
23713         return date >= this.startDate && date <= this.endDate;
23714     },
23715
23716     
23717     remove: function() 
23718     {
23719         this.picker().remove();
23720     },
23721     
23722     validateValue : function(value)
23723     {
23724         if(this.getVisibilityEl().hasClass('hidden')){
23725             return true;
23726         }
23727         
23728         if(value.length < 1)  {
23729             if(this.allowBlank){
23730                 return true;
23731             }
23732             return false;
23733         }
23734         
23735         if(value.length < this.minLength){
23736             return false;
23737         }
23738         if(value.length > this.maxLength){
23739             return false;
23740         }
23741         if(this.vtype){
23742             var vt = Roo.form.VTypes;
23743             if(!vt[this.vtype](value, this)){
23744                 return false;
23745             }
23746         }
23747         if(typeof this.validator == "function"){
23748             var msg = this.validator(value);
23749             if(msg !== true){
23750                 return false;
23751             }
23752         }
23753         
23754         if(this.regex && !this.regex.test(value)){
23755             return false;
23756         }
23757         
23758         if(typeof(this.parseDate(value)) == 'undefined'){
23759             return false;
23760         }
23761         
23762         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23763             return false;
23764         }      
23765         
23766         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23767             return false;
23768         } 
23769         
23770         
23771         return true;
23772     },
23773     
23774     reset : function()
23775     {
23776         this.date = this.viewDate = '';
23777         
23778         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23779     }
23780    
23781 });
23782
23783 Roo.apply(Roo.bootstrap.form.DateField,  {
23784     
23785     head : {
23786         tag: 'thead',
23787         cn: [
23788         {
23789             tag: 'tr',
23790             cn: [
23791             {
23792                 tag: 'th',
23793                 cls: 'prev',
23794                 html: '<i class="fa fa-arrow-left"/>'
23795             },
23796             {
23797                 tag: 'th',
23798                 cls: 'switch',
23799                 colspan: '5'
23800             },
23801             {
23802                 tag: 'th',
23803                 cls: 'next',
23804                 html: '<i class="fa fa-arrow-right"/>'
23805             }
23806
23807             ]
23808         }
23809         ]
23810     },
23811     
23812     content : {
23813         tag: 'tbody',
23814         cn: [
23815         {
23816             tag: 'tr',
23817             cn: [
23818             {
23819                 tag: 'td',
23820                 colspan: '7'
23821             }
23822             ]
23823         }
23824         ]
23825     },
23826     
23827     footer : {
23828         tag: 'tfoot',
23829         cn: [
23830         {
23831             tag: 'tr',
23832             cn: [
23833             {
23834                 tag: 'th',
23835                 colspan: '7',
23836                 cls: 'today'
23837             }
23838                     
23839             ]
23840         }
23841         ]
23842     },
23843     
23844     dates:{
23845         en: {
23846             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23847             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23848             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23849             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23850             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23851             today: "Today"
23852         }
23853     },
23854     
23855     modes: [
23856     {
23857         clsName: 'days',
23858         navFnc: 'Month',
23859         navStep: 1
23860     },
23861     {
23862         clsName: 'months',
23863         navFnc: 'FullYear',
23864         navStep: 1
23865     },
23866     {
23867         clsName: 'years',
23868         navFnc: 'FullYear',
23869         navStep: 10
23870     }]
23871 });
23872
23873 Roo.apply(Roo.bootstrap.form.DateField,  {
23874   
23875     template : {
23876         tag: 'div',
23877         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23878         cn: [
23879         {
23880             tag: 'div',
23881             cls: 'datepicker-days',
23882             cn: [
23883             {
23884                 tag: 'table',
23885                 cls: 'table-condensed',
23886                 cn:[
23887                 Roo.bootstrap.form.DateField.head,
23888                 {
23889                     tag: 'tbody'
23890                 },
23891                 Roo.bootstrap.form.DateField.footer
23892                 ]
23893             }
23894             ]
23895         },
23896         {
23897             tag: 'div',
23898             cls: 'datepicker-months',
23899             cn: [
23900             {
23901                 tag: 'table',
23902                 cls: 'table-condensed',
23903                 cn:[
23904                 Roo.bootstrap.form.DateField.head,
23905                 Roo.bootstrap.form.DateField.content,
23906                 Roo.bootstrap.form.DateField.footer
23907                 ]
23908             }
23909             ]
23910         },
23911         {
23912             tag: 'div',
23913             cls: 'datepicker-years',
23914             cn: [
23915             {
23916                 tag: 'table',
23917                 cls: 'table-condensed',
23918                 cn:[
23919                 Roo.bootstrap.form.DateField.head,
23920                 Roo.bootstrap.form.DateField.content,
23921                 Roo.bootstrap.form.DateField.footer
23922                 ]
23923             }
23924             ]
23925         }
23926         ]
23927     }
23928 });
23929
23930  
23931
23932  /*
23933  * - LGPL
23934  *
23935  * TimeField
23936  * 
23937  */
23938
23939 /**
23940  * @class Roo.bootstrap.form.TimeField
23941  * @extends Roo.bootstrap.form.Input
23942  * Bootstrap DateField class
23943  * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23944  * 
23945  * 
23946  * @constructor
23947  * Create a new TimeField
23948  * @param {Object} config The config object
23949  */
23950
23951 Roo.bootstrap.form.TimeField = function(config){
23952     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23953     this.addEvents({
23954             /**
23955              * @event show
23956              * Fires when this field show.
23957              * @param {Roo.bootstrap.form.DateField} thisthis
23958              * @param {Mixed} date The date value
23959              */
23960             show : true,
23961             /**
23962              * @event show
23963              * Fires when this field hide.
23964              * @param {Roo.bootstrap.form.DateField} this
23965              * @param {Mixed} date The date value
23966              */
23967             hide : true,
23968             /**
23969              * @event select
23970              * Fires when select a date.
23971              * @param {Roo.bootstrap.form.DateField} this
23972              * @param {Mixed} date The date value
23973              */
23974             select : true
23975         });
23976 };
23977
23978 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23979     
23980     /**
23981      * @cfg {String} format
23982      * The default time format string which can be overriden for localization support.  The format must be
23983      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23984      */
23985     format : "H:i",
23986     minuteStep : 1,
23987
23988     getAutoCreate : function()
23989     {
23990         this.after = '<i class="fa far fa-clock"></i>';
23991         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23992         
23993          
23994     },
23995     onRender: function(ct, position)
23996     {
23997         
23998         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23999                 
24000         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24001         
24002         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24003         
24004         this.pop = this.picker().select('>.datepicker-time',true).first();
24005         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24006         
24007         this.picker().on('mousedown', this.onMousedown, this);
24008         this.picker().on('click', this.onClick, this);
24009         
24010         this.picker().addClass('datepicker-dropdown');
24011     
24012         this.fillTime();
24013         this.update();
24014             
24015         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24016         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24017         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24018         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24019         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24020         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24021
24022     },
24023     
24024     fireKey: function(e){
24025         if (!this.picker().isVisible()){
24026             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24027                 this.show();
24028             }
24029             return;
24030         }
24031
24032         e.preventDefault();
24033         
24034         switch(e.keyCode){
24035             case 27: // escape
24036                 this.hide();
24037                 break;
24038             case 37: // left
24039             case 39: // right
24040                 this.onTogglePeriod();
24041                 break;
24042             case 38: // up
24043                 this.onIncrementMinutes();
24044                 break;
24045             case 40: // down
24046                 this.onDecrementMinutes();
24047                 break;
24048             case 13: // enter
24049             case 9: // tab
24050                 this.setTime();
24051                 break;
24052         }
24053     },
24054     
24055     onClick: function(e) {
24056         e.stopPropagation();
24057         e.preventDefault();
24058     },
24059     
24060     picker : function()
24061     {
24062         return this.pickerEl;
24063     },
24064     
24065     fillTime: function()
24066     {    
24067         var time = this.pop.select('tbody', true).first();
24068         
24069         time.dom.innerHTML = '';
24070         
24071         time.createChild({
24072             tag: 'tr',
24073             cn: [
24074                 {
24075                     tag: 'td',
24076                     cn: [
24077                         {
24078                             tag: 'a',
24079                             href: '#',
24080                             cls: 'btn',
24081                             cn: [
24082                                 {
24083                                     tag: 'i',
24084                                     cls: 'hours-up fa fas fa-chevron-up'
24085                                 }
24086                             ]
24087                         } 
24088                     ]
24089                 },
24090                 {
24091                     tag: 'td',
24092                     cls: 'separator'
24093                 },
24094                 {
24095                     tag: 'td',
24096                     cn: [
24097                         {
24098                             tag: 'a',
24099                             href: '#',
24100                             cls: 'btn',
24101                             cn: [
24102                                 {
24103                                     tag: 'i',
24104                                     cls: 'minutes-up fa fas fa-chevron-up'
24105                                 }
24106                             ]
24107                         }
24108                     ]
24109                 },
24110                 {
24111                     tag: 'td',
24112                     cls: 'separator'
24113                 }
24114             ]
24115         });
24116         
24117         time.createChild({
24118             tag: 'tr',
24119             cn: [
24120                 {
24121                     tag: 'td',
24122                     cn: [
24123                         {
24124                             tag: 'span',
24125                             cls: 'timepicker-hour',
24126                             html: '00'
24127                         }  
24128                     ]
24129                 },
24130                 {
24131                     tag: 'td',
24132                     cls: 'separator',
24133                     html: ':'
24134                 },
24135                 {
24136                     tag: 'td',
24137                     cn: [
24138                         {
24139                             tag: 'span',
24140                             cls: 'timepicker-minute',
24141                             html: '00'
24142                         }  
24143                     ]
24144                 },
24145                 {
24146                     tag: 'td',
24147                     cls: 'separator'
24148                 },
24149                 {
24150                     tag: 'td',
24151                     cn: [
24152                         {
24153                             tag: 'button',
24154                             type: 'button',
24155                             cls: 'btn btn-primary period',
24156                             html: 'AM'
24157                             
24158                         }
24159                     ]
24160                 }
24161             ]
24162         });
24163         
24164         time.createChild({
24165             tag: 'tr',
24166             cn: [
24167                 {
24168                     tag: 'td',
24169                     cn: [
24170                         {
24171                             tag: 'a',
24172                             href: '#',
24173                             cls: 'btn',
24174                             cn: [
24175                                 {
24176                                     tag: 'span',
24177                                     cls: 'hours-down fa fas fa-chevron-down'
24178                                 }
24179                             ]
24180                         }
24181                     ]
24182                 },
24183                 {
24184                     tag: 'td',
24185                     cls: 'separator'
24186                 },
24187                 {
24188                     tag: 'td',
24189                     cn: [
24190                         {
24191                             tag: 'a',
24192                             href: '#',
24193                             cls: 'btn',
24194                             cn: [
24195                                 {
24196                                     tag: 'span',
24197                                     cls: 'minutes-down fa fas fa-chevron-down'
24198                                 }
24199                             ]
24200                         }
24201                     ]
24202                 },
24203                 {
24204                     tag: 'td',
24205                     cls: 'separator'
24206                 }
24207             ]
24208         });
24209         
24210     },
24211     
24212     update: function()
24213     {
24214         // default minute is a multiple of minuteStep
24215         if(typeof(this.time) === 'undefined') {
24216             this.time = new Date();
24217             this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24218         }
24219         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24220         
24221         this.fill();
24222     },
24223     
24224     fill: function() 
24225     {
24226         var hours = this.time.getHours();
24227         var minutes = this.time.getMinutes();
24228         var period = 'AM';
24229         
24230         if(hours > 11){
24231             period = 'PM';
24232         }
24233         
24234         if(hours == 0){
24235             hours = 12;
24236         }
24237         
24238         
24239         if(hours > 12){
24240             hours = hours - 12;
24241         }
24242         
24243         if(hours < 10){
24244             hours = '0' + hours;
24245         }
24246         
24247         if(minutes < 10){
24248             minutes = '0' + minutes;
24249         }
24250         
24251         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24252         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24253         this.pop.select('button', true).first().dom.innerHTML = period;
24254         
24255     },
24256     
24257     place: function()
24258     {   
24259         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24260         
24261         var cls = ['bottom'];
24262         
24263         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24264             cls.pop();
24265             cls.push('top');
24266         }
24267         
24268         cls.push('right');
24269         
24270         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24271             cls.pop();
24272             cls.push('left');
24273         }
24274         //this.picker().setXY(20000,20000);
24275         this.picker().addClass(cls.join('-'));
24276         
24277         var _this = this;
24278         
24279         Roo.each(cls, function(c){
24280             if(c == 'bottom'){
24281                 (function() {
24282                  //  
24283                 }).defer(200);
24284                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24285                 //_this.picker().setTop(_this.inputEl().getHeight());
24286                 return;
24287             }
24288             if(c == 'top'){
24289                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24290                 
24291                 //_this.picker().setTop(0 - _this.picker().getHeight());
24292                 return;
24293             }
24294             /*
24295             if(c == 'left'){
24296                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24297                 return;
24298             }
24299             if(c == 'right'){
24300                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24301                 return;
24302             }
24303             */
24304         });
24305         
24306     },
24307   
24308     onFocus : function()
24309     {
24310         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24311         this.show();
24312     },
24313     
24314     onBlur : function()
24315     {
24316         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24317         this.hide();
24318     },
24319     
24320     show : function()
24321     {
24322         this.picker().show();
24323         this.pop.show();
24324         this.update();
24325         this.place();
24326         
24327         this.fireEvent('show', this, this.date);
24328     },
24329     
24330     hide : function()
24331     {
24332         this.picker().hide();
24333         this.pop.hide();
24334         
24335         this.fireEvent('hide', this, this.date);
24336     },
24337     
24338     setTime : function()
24339     {
24340         this.hide();
24341         this.setValue(this.time.format(this.format));
24342         
24343         this.fireEvent('select', this, this.date);
24344         
24345         
24346     },
24347     
24348     onMousedown: function(e){
24349         e.stopPropagation();
24350         e.preventDefault();
24351     },
24352     
24353     onIncrementHours: function()
24354     {
24355         Roo.log('onIncrementHours');
24356         this.time = this.time.add(Date.HOUR, 1);
24357         this.update();
24358         
24359     },
24360     
24361     onDecrementHours: function()
24362     {
24363         Roo.log('onDecrementHours');
24364         this.time = this.time.add(Date.HOUR, -1);
24365         this.update();
24366     },
24367     
24368     onIncrementMinutes: function()
24369     {
24370         Roo.log('onIncrementMinutes');
24371         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24372         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24373         this.update();
24374     },
24375     
24376     onDecrementMinutes: function()
24377     {
24378         Roo.log('onDecrementMinutes');
24379         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24380         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24381         this.update();
24382     },
24383     
24384     onTogglePeriod: function()
24385     {
24386         Roo.log('onTogglePeriod');
24387         this.time = this.time.add(Date.HOUR, 12);
24388         this.update();
24389     }
24390     
24391    
24392 });
24393  
24394
24395 Roo.apply(Roo.bootstrap.form.TimeField,  {
24396   
24397     template : {
24398         tag: 'div',
24399         cls: 'datepicker dropdown-menu',
24400         cn: [
24401             {
24402                 tag: 'div',
24403                 cls: 'datepicker-time',
24404                 cn: [
24405                 {
24406                     tag: 'table',
24407                     cls: 'table-condensed',
24408                     cn:[
24409                         {
24410                             tag: 'tbody',
24411                             cn: [
24412                                 {
24413                                     tag: 'tr',
24414                                     cn: [
24415                                     {
24416                                         tag: 'td',
24417                                         colspan: '7'
24418                                     }
24419                                     ]
24420                                 }
24421                             ]
24422                         },
24423                         {
24424                             tag: 'tfoot',
24425                             cn: [
24426                                 {
24427                                     tag: 'tr',
24428                                     cn: [
24429                                     {
24430                                         tag: 'th',
24431                                         colspan: '7',
24432                                         cls: '',
24433                                         cn: [
24434                                             {
24435                                                 tag: 'button',
24436                                                 cls: 'btn btn-info ok',
24437                                                 html: 'OK'
24438                                             }
24439                                         ]
24440                                     }
24441                     
24442                                     ]
24443                                 }
24444                             ]
24445                         }
24446                     ]
24447                 }
24448                 ]
24449             }
24450         ]
24451     }
24452 });
24453
24454  
24455
24456  /*
24457  * - LGPL
24458  *
24459  * MonthField
24460  * 
24461  */
24462
24463 /**
24464  * @class Roo.bootstrap.form.MonthField
24465  * @extends Roo.bootstrap.form.Input
24466  * Bootstrap MonthField class
24467  * 
24468  * @cfg {String} language default en
24469  * 
24470  * @constructor
24471  * Create a new MonthField
24472  * @param {Object} config The config object
24473  */
24474
24475 Roo.bootstrap.form.MonthField = function(config){
24476     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24477     
24478     this.addEvents({
24479         /**
24480          * @event show
24481          * Fires when this field show.
24482          * @param {Roo.bootstrap.form.MonthField} this
24483          * @param {Mixed} date The date value
24484          */
24485         show : true,
24486         /**
24487          * @event show
24488          * Fires when this field hide.
24489          * @param {Roo.bootstrap.form.MonthField} this
24490          * @param {Mixed} date The date value
24491          */
24492         hide : true,
24493         /**
24494          * @event select
24495          * Fires when select a date.
24496          * @param {Roo.bootstrap.form.MonthField} this
24497          * @param {String} oldvalue The old value
24498          * @param {String} newvalue The new value
24499          */
24500         select : true
24501     });
24502 };
24503
24504 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24505     
24506     onRender: function(ct, position)
24507     {
24508         
24509         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24510         
24511         this.language = this.language || 'en';
24512         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24513         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24514         
24515         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24516         this.isInline = false;
24517         this.isInput = true;
24518         this.component = this.el.select('.add-on', true).first() || false;
24519         this.component = (this.component && this.component.length === 0) ? false : this.component;
24520         this.hasInput = this.component && this.inputEL().length;
24521         
24522         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24523         
24524         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24525         
24526         this.picker().on('mousedown', this.onMousedown, this);
24527         this.picker().on('click', this.onClick, this);
24528         
24529         this.picker().addClass('datepicker-dropdown');
24530         
24531         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24532             v.setStyle('width', '189px');
24533         });
24534         
24535         this.fillMonths();
24536         
24537         this.update();
24538         
24539         if(this.isInline) {
24540             this.show();
24541         }
24542         
24543     },
24544     
24545     setValue: function(v, suppressEvent)
24546     {   
24547         var o = this.getValue();
24548         
24549         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24550         
24551         this.update();
24552
24553         if(suppressEvent !== true){
24554             this.fireEvent('select', this, o, v);
24555         }
24556         
24557     },
24558     
24559     getValue: function()
24560     {
24561         return this.value;
24562     },
24563     
24564     onClick: function(e) 
24565     {
24566         e.stopPropagation();
24567         e.preventDefault();
24568         
24569         var target = e.getTarget();
24570         
24571         if(target.nodeName.toLowerCase() === 'i'){
24572             target = Roo.get(target).dom.parentNode;
24573         }
24574         
24575         var nodeName = target.nodeName;
24576         var className = target.className;
24577         var html = target.innerHTML;
24578         
24579         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24580             return;
24581         }
24582         
24583         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24584         
24585         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24586         
24587         this.hide();
24588                         
24589     },
24590     
24591     picker : function()
24592     {
24593         return this.pickerEl;
24594     },
24595     
24596     fillMonths: function()
24597     {    
24598         var i = 0;
24599         var months = this.picker().select('>.datepicker-months td', true).first();
24600         
24601         months.dom.innerHTML = '';
24602         
24603         while (i < 12) {
24604             var month = {
24605                 tag: 'span',
24606                 cls: 'month',
24607                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24608             };
24609             
24610             months.createChild(month);
24611         }
24612         
24613     },
24614     
24615     update: function()
24616     {
24617         var _this = this;
24618         
24619         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24620             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24621         }
24622         
24623         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24624             e.removeClass('active');
24625             
24626             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24627                 e.addClass('active');
24628             }
24629         })
24630     },
24631     
24632     place: function()
24633     {
24634         if(this.isInline) {
24635             return;
24636         }
24637         
24638         this.picker().removeClass(['bottom', 'top']);
24639         
24640         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24641             /*
24642              * place to the top of element!
24643              *
24644              */
24645             
24646             this.picker().addClass('top');
24647             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24648             
24649             return;
24650         }
24651         
24652         this.picker().addClass('bottom');
24653         
24654         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24655     },
24656     
24657     onFocus : function()
24658     {
24659         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24660         this.show();
24661     },
24662     
24663     onBlur : function()
24664     {
24665         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24666         
24667         var d = this.inputEl().getValue();
24668         
24669         this.setValue(d);
24670                 
24671         this.hide();
24672     },
24673     
24674     show : function()
24675     {
24676         this.picker().show();
24677         this.picker().select('>.datepicker-months', true).first().show();
24678         this.update();
24679         this.place();
24680         
24681         this.fireEvent('show', this, this.date);
24682     },
24683     
24684     hide : function()
24685     {
24686         if(this.isInline) {
24687             return;
24688         }
24689         this.picker().hide();
24690         this.fireEvent('hide', this, this.date);
24691         
24692     },
24693     
24694     onMousedown: function(e)
24695     {
24696         e.stopPropagation();
24697         e.preventDefault();
24698     },
24699     
24700     keyup: function(e)
24701     {
24702         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24703         this.update();
24704     },
24705
24706     fireKey: function(e)
24707     {
24708         if (!this.picker().isVisible()){
24709             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24710                 this.show();
24711             }
24712             return;
24713         }
24714         
24715         var dir;
24716         
24717         switch(e.keyCode){
24718             case 27: // escape
24719                 this.hide();
24720                 e.preventDefault();
24721                 break;
24722             case 37: // left
24723             case 39: // right
24724                 dir = e.keyCode == 37 ? -1 : 1;
24725                 
24726                 this.vIndex = this.vIndex + dir;
24727                 
24728                 if(this.vIndex < 0){
24729                     this.vIndex = 0;
24730                 }
24731                 
24732                 if(this.vIndex > 11){
24733                     this.vIndex = 11;
24734                 }
24735                 
24736                 if(isNaN(this.vIndex)){
24737                     this.vIndex = 0;
24738                 }
24739                 
24740                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24741                 
24742                 break;
24743             case 38: // up
24744             case 40: // down
24745                 
24746                 dir = e.keyCode == 38 ? -1 : 1;
24747                 
24748                 this.vIndex = this.vIndex + dir * 4;
24749                 
24750                 if(this.vIndex < 0){
24751                     this.vIndex = 0;
24752                 }
24753                 
24754                 if(this.vIndex > 11){
24755                     this.vIndex = 11;
24756                 }
24757                 
24758                 if(isNaN(this.vIndex)){
24759                     this.vIndex = 0;
24760                 }
24761                 
24762                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24763                 break;
24764                 
24765             case 13: // enter
24766                 
24767                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24768                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24769                 }
24770                 
24771                 this.hide();
24772                 e.preventDefault();
24773                 break;
24774             case 9: // tab
24775                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24776                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24777                 }
24778                 this.hide();
24779                 break;
24780             case 16: // shift
24781             case 17: // ctrl
24782             case 18: // alt
24783                 break;
24784             default :
24785                 this.hide();
24786                 
24787         }
24788     },
24789     
24790     remove: function() 
24791     {
24792         this.picker().remove();
24793     }
24794    
24795 });
24796
24797 Roo.apply(Roo.bootstrap.form.MonthField,  {
24798     
24799     content : {
24800         tag: 'tbody',
24801         cn: [
24802         {
24803             tag: 'tr',
24804             cn: [
24805             {
24806                 tag: 'td',
24807                 colspan: '7'
24808             }
24809             ]
24810         }
24811         ]
24812     },
24813     
24814     dates:{
24815         en: {
24816             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24817             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24818         }
24819     }
24820 });
24821
24822 Roo.apply(Roo.bootstrap.form.MonthField,  {
24823   
24824     template : {
24825         tag: 'div',
24826         cls: 'datepicker dropdown-menu roo-dynamic',
24827         cn: [
24828             {
24829                 tag: 'div',
24830                 cls: 'datepicker-months',
24831                 cn: [
24832                 {
24833                     tag: 'table',
24834                     cls: 'table-condensed',
24835                     cn:[
24836                         Roo.bootstrap.form.DateField.content
24837                     ]
24838                 }
24839                 ]
24840             }
24841         ]
24842     }
24843 });
24844
24845  
24846
24847  
24848  /*
24849  * - LGPL
24850  *
24851  * CheckBox
24852  * 
24853  */
24854
24855 /**
24856  * @class Roo.bootstrap.form.CheckBox
24857  * @extends Roo.bootstrap.form.Input
24858  * Bootstrap CheckBox class
24859  * 
24860  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24861  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24862  * @cfg {String} boxLabel The text that appears beside the checkbox
24863  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24864  * @cfg {Boolean} checked initnal the element
24865  * @cfg {Boolean} inline inline the element (default false)
24866  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24867  * @cfg {String} tooltip label tooltip
24868  * 
24869  * @constructor
24870  * Create a new CheckBox
24871  * @param {Object} config The config object
24872  */
24873
24874 Roo.bootstrap.form.CheckBox = function(config){
24875     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24876    
24877     this.addEvents({
24878         /**
24879         * @event check
24880         * Fires when the element is checked or unchecked.
24881         * @param {Roo.bootstrap.form.CheckBox} this This input
24882         * @param {Boolean} checked The new checked value
24883         */
24884        check : true,
24885        /**
24886         * @event click
24887         * Fires when the element is click.
24888         * @param {Roo.bootstrap.form.CheckBox} this This input
24889         */
24890        click : true
24891     });
24892     
24893 };
24894
24895 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24896   
24897     inputType: 'checkbox',
24898     inputValue: 1,
24899     valueOff: 0,
24900     boxLabel: false,
24901     checked: false,
24902     weight : false,
24903     inline: false,
24904     tooltip : '',
24905     
24906     // checkbox success does not make any sense really.. 
24907     invalidClass : "",
24908     validClass : "",
24909     
24910     
24911     getAutoCreate : function()
24912     {
24913         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24914         
24915         var id = Roo.id();
24916         
24917         var cfg = {};
24918         
24919         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24920         
24921         if(this.inline){
24922             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24923         }
24924         
24925         var input =  {
24926             tag: 'input',
24927             id : id,
24928             type : this.inputType,
24929             value : this.inputValue,
24930             cls : 'roo-' + this.inputType, //'form-box',
24931             placeholder : this.placeholder || ''
24932             
24933         };
24934         
24935         if(this.inputType != 'radio'){
24936             var hidden =  {
24937                 tag: 'input',
24938                 type : 'hidden',
24939                 cls : 'roo-hidden-value',
24940                 value : this.checked ? this.inputValue : this.valueOff
24941             };
24942         }
24943         
24944             
24945         if (this.weight) { // Validity check?
24946             cfg.cls += " " + this.inputType + "-" + this.weight;
24947         }
24948         
24949         if (this.disabled) {
24950             input.disabled=true;
24951         }
24952         
24953         if(this.checked){
24954             input.checked = this.checked;
24955         }
24956         
24957         if (this.name) {
24958             
24959             input.name = this.name;
24960             
24961             if(this.inputType != 'radio'){
24962                 hidden.name = this.name;
24963                 input.name = '_hidden_' + this.name;
24964             }
24965         }
24966         
24967         if (this.size) {
24968             input.cls += ' input-' + this.size;
24969         }
24970         
24971         var settings=this;
24972         
24973         ['xs','sm','md','lg'].map(function(size){
24974             if (settings[size]) {
24975                 cfg.cls += ' col-' + size + '-' + settings[size];
24976             }
24977         });
24978         
24979         var inputblock = input;
24980          
24981         if (this.before || this.after) {
24982             
24983             inputblock = {
24984                 cls : 'input-group',
24985                 cn :  [] 
24986             };
24987             
24988             if (this.before) {
24989                 inputblock.cn.push({
24990                     tag :'span',
24991                     cls : 'input-group-addon',
24992                     html : this.before
24993                 });
24994             }
24995             
24996             inputblock.cn.push(input);
24997             
24998             if(this.inputType != 'radio'){
24999                 inputblock.cn.push(hidden);
25000             }
25001             
25002             if (this.after) {
25003                 inputblock.cn.push({
25004                     tag :'span',
25005                     cls : 'input-group-addon',
25006                     html : this.after
25007                 });
25008             }
25009             
25010         }
25011         var boxLabelCfg = false;
25012         
25013         if(this.boxLabel){
25014            
25015             boxLabelCfg = {
25016                 tag: 'label',
25017                 //'for': id, // box label is handled by onclick - so no for...
25018                 cls: 'box-label',
25019                 html: this.boxLabel
25020             };
25021             if(this.tooltip){
25022                 boxLabelCfg.tooltip = this.tooltip;
25023             }
25024              
25025         }
25026         
25027         
25028         if (align ==='left' && this.fieldLabel.length) {
25029 //                Roo.log("left and has label");
25030             cfg.cn = [
25031                 {
25032                     tag: 'label',
25033                     'for' :  id,
25034                     cls : 'control-label',
25035                     html : this.fieldLabel
25036                 },
25037                 {
25038                     cls : "", 
25039                     cn: [
25040                         inputblock
25041                     ]
25042                 }
25043             ];
25044             
25045             if (boxLabelCfg) {
25046                 cfg.cn[1].cn.push(boxLabelCfg);
25047             }
25048             
25049             if(this.labelWidth > 12){
25050                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25051             }
25052             
25053             if(this.labelWidth < 13 && this.labelmd == 0){
25054                 this.labelmd = this.labelWidth;
25055             }
25056             
25057             if(this.labellg > 0){
25058                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25059                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25060             }
25061             
25062             if(this.labelmd > 0){
25063                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25064                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25065             }
25066             
25067             if(this.labelsm > 0){
25068                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25069                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25070             }
25071             
25072             if(this.labelxs > 0){
25073                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25074                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25075             }
25076             
25077         } else if ( this.fieldLabel.length) {
25078 //                Roo.log(" label");
25079                 cfg.cn = [
25080                    
25081                     {
25082                         tag: this.boxLabel ? 'span' : 'label',
25083                         'for': id,
25084                         cls: 'control-label box-input-label',
25085                         //cls : 'input-group-addon',
25086                         html : this.fieldLabel
25087                     },
25088                     
25089                     inputblock
25090                     
25091                 ];
25092                 if (boxLabelCfg) {
25093                     cfg.cn.push(boxLabelCfg);
25094                 }
25095
25096         } else {
25097             
25098 //                Roo.log(" no label && no align");
25099                 cfg.cn = [  inputblock ] ;
25100                 if (boxLabelCfg) {
25101                     cfg.cn.push(boxLabelCfg);
25102                 }
25103
25104                 
25105         }
25106         
25107        
25108         
25109         if(this.inputType != 'radio'){
25110             cfg.cn.push(hidden);
25111         }
25112         
25113         return cfg;
25114         
25115     },
25116     
25117     /**
25118      * return the real input element.
25119      */
25120     inputEl: function ()
25121     {
25122         return this.el.select('input.roo-' + this.inputType,true).first();
25123     },
25124     hiddenEl: function ()
25125     {
25126         return this.el.select('input.roo-hidden-value',true).first();
25127     },
25128     
25129     labelEl: function()
25130     {
25131         return this.el.select('label.control-label',true).first();
25132     },
25133     /* depricated... */
25134     
25135     label: function()
25136     {
25137         return this.labelEl();
25138     },
25139     
25140     boxLabelEl: function()
25141     {
25142         return this.el.select('label.box-label',true).first();
25143     },
25144     
25145     initEvents : function()
25146     {
25147 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25148         
25149         this.inputEl().on('click', this.onClick,  this);
25150         
25151         if (this.boxLabel) { 
25152             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25153         }
25154         
25155         this.startValue = this.getValue();
25156         
25157         if(this.groupId){
25158             Roo.bootstrap.form.CheckBox.register(this);
25159         }
25160     },
25161     
25162     onClick : function(e)
25163     {   
25164         if(this.fireEvent('click', this, e) !== false){
25165             this.setChecked(!this.checked);
25166         }
25167         
25168     },
25169     
25170     setChecked : function(state,suppressEvent)
25171     {
25172         this.startValue = this.getValue();
25173
25174         if(this.inputType == 'radio'){
25175             
25176             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25177                 e.dom.checked = false;
25178             });
25179             
25180             this.inputEl().dom.checked = true;
25181             
25182             this.inputEl().dom.value = this.inputValue;
25183             
25184             if(suppressEvent !== true){
25185                 this.fireEvent('check', this, true);
25186             }
25187             
25188             this.validate();
25189             
25190             return;
25191         }
25192         
25193         this.checked = state;
25194         
25195         this.inputEl().dom.checked = state;
25196         
25197         
25198         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25199         
25200         if(suppressEvent !== true){
25201             this.fireEvent('check', this, state);
25202         }
25203         
25204         this.validate();
25205     },
25206     
25207     getValue : function()
25208     {
25209         if(this.inputType == 'radio'){
25210             return this.getGroupValue();
25211         }
25212         
25213         return this.hiddenEl().dom.value;
25214         
25215     },
25216     
25217     getGroupValue : function()
25218     {
25219         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25220             return '';
25221         }
25222         
25223         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25224     },
25225     
25226     setValue : function(v,suppressEvent)
25227     {
25228         if(this.inputType == 'radio'){
25229             this.setGroupValue(v, suppressEvent);
25230             return;
25231         }
25232         
25233         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25234         
25235         this.validate();
25236     },
25237     
25238     setGroupValue : function(v, suppressEvent)
25239     {
25240         this.startValue = this.getValue();
25241         
25242         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25243             e.dom.checked = false;
25244             
25245             if(e.dom.value == v){
25246                 e.dom.checked = true;
25247             }
25248         });
25249         
25250         if(suppressEvent !== true){
25251             this.fireEvent('check', this, true);
25252         }
25253
25254         this.validate();
25255         
25256         return;
25257     },
25258     
25259     validate : function()
25260     {
25261         if(this.getVisibilityEl().hasClass('hidden')){
25262             return true;
25263         }
25264         
25265         if(
25266                 this.disabled || 
25267                 (this.inputType == 'radio' && this.validateRadio()) ||
25268                 (this.inputType == 'checkbox' && this.validateCheckbox())
25269         ){
25270             this.markValid();
25271             return true;
25272         }
25273         
25274         this.markInvalid();
25275         return false;
25276     },
25277     
25278     validateRadio : function()
25279     {
25280         if(this.getVisibilityEl().hasClass('hidden')){
25281             return true;
25282         }
25283         
25284         if(this.allowBlank){
25285             return true;
25286         }
25287         
25288         var valid = false;
25289         
25290         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25291             if(!e.dom.checked){
25292                 return;
25293             }
25294             
25295             valid = true;
25296             
25297             return false;
25298         });
25299         
25300         return valid;
25301     },
25302     
25303     validateCheckbox : function()
25304     {
25305         if(!this.groupId){
25306             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25307             //return (this.getValue() == this.inputValue) ? true : false;
25308         }
25309         
25310         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25311         
25312         if(!group){
25313             return false;
25314         }
25315         
25316         var r = false;
25317         
25318         for(var i in group){
25319             if(group[i].el.isVisible(true)){
25320                 r = false;
25321                 break;
25322             }
25323             
25324             r = true;
25325         }
25326         
25327         for(var i in group){
25328             if(r){
25329                 break;
25330             }
25331             
25332             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25333         }
25334         
25335         return r;
25336     },
25337     
25338     /**
25339      * Mark this field as valid
25340      */
25341     markValid : function()
25342     {
25343         var _this = this;
25344         
25345         this.fireEvent('valid', this);
25346         
25347         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25348         
25349         if(this.groupId){
25350             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25351         }
25352         
25353         if(label){
25354             label.markValid();
25355         }
25356
25357         if(this.inputType == 'radio'){
25358             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25359                 var fg = e.findParent('.form-group', false, true);
25360                 if (Roo.bootstrap.version == 3) {
25361                     fg.removeClass([_this.invalidClass, _this.validClass]);
25362                     fg.addClass(_this.validClass);
25363                 } else {
25364                     fg.removeClass(['is-valid', 'is-invalid']);
25365                     fg.addClass('is-valid');
25366                 }
25367             });
25368             
25369             return;
25370         }
25371
25372         if(!this.groupId){
25373             var fg = this.el.findParent('.form-group', false, true);
25374             if (Roo.bootstrap.version == 3) {
25375                 fg.removeClass([this.invalidClass, this.validClass]);
25376                 fg.addClass(this.validClass);
25377             } else {
25378                 fg.removeClass(['is-valid', 'is-invalid']);
25379                 fg.addClass('is-valid');
25380             }
25381             return;
25382         }
25383         
25384         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25385         
25386         if(!group){
25387             return;
25388         }
25389         
25390         for(var i in group){
25391             var fg = group[i].el.findParent('.form-group', false, true);
25392             if (Roo.bootstrap.version == 3) {
25393                 fg.removeClass([this.invalidClass, this.validClass]);
25394                 fg.addClass(this.validClass);
25395             } else {
25396                 fg.removeClass(['is-valid', 'is-invalid']);
25397                 fg.addClass('is-valid');
25398             }
25399         }
25400     },
25401     
25402      /**
25403      * Mark this field as invalid
25404      * @param {String} msg The validation message
25405      */
25406     markInvalid : function(msg)
25407     {
25408         if(this.allowBlank){
25409             return;
25410         }
25411         
25412         var _this = this;
25413         
25414         this.fireEvent('invalid', this, msg);
25415         
25416         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25417         
25418         if(this.groupId){
25419             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25420         }
25421         
25422         if(label){
25423             label.markInvalid();
25424         }
25425             
25426         if(this.inputType == 'radio'){
25427             
25428             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25429                 var fg = e.findParent('.form-group', false, true);
25430                 if (Roo.bootstrap.version == 3) {
25431                     fg.removeClass([_this.invalidClass, _this.validClass]);
25432                     fg.addClass(_this.invalidClass);
25433                 } else {
25434                     fg.removeClass(['is-invalid', 'is-valid']);
25435                     fg.addClass('is-invalid');
25436                 }
25437             });
25438             
25439             return;
25440         }
25441         
25442         if(!this.groupId){
25443             var fg = this.el.findParent('.form-group', false, true);
25444             if (Roo.bootstrap.version == 3) {
25445                 fg.removeClass([_this.invalidClass, _this.validClass]);
25446                 fg.addClass(_this.invalidClass);
25447             } else {
25448                 fg.removeClass(['is-invalid', 'is-valid']);
25449                 fg.addClass('is-invalid');
25450             }
25451             return;
25452         }
25453         
25454         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25455         
25456         if(!group){
25457             return;
25458         }
25459         
25460         for(var i in group){
25461             var fg = group[i].el.findParent('.form-group', false, true);
25462             if (Roo.bootstrap.version == 3) {
25463                 fg.removeClass([_this.invalidClass, _this.validClass]);
25464                 fg.addClass(_this.invalidClass);
25465             } else {
25466                 fg.removeClass(['is-invalid', 'is-valid']);
25467                 fg.addClass('is-invalid');
25468             }
25469         }
25470         
25471     },
25472     
25473     clearInvalid : function()
25474     {
25475         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25476         
25477         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25478         
25479         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25480         
25481         if (label && label.iconEl) {
25482             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25483             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25484         }
25485     },
25486     
25487     disable : function()
25488     {
25489         if(this.inputType != 'radio'){
25490             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25491             return;
25492         }
25493         
25494         var _this = this;
25495         
25496         if(this.rendered){
25497             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25498                 _this.getActionEl().addClass(this.disabledClass);
25499                 e.dom.disabled = true;
25500             });
25501         }
25502         
25503         this.disabled = true;
25504         this.fireEvent("disable", this);
25505         return this;
25506     },
25507
25508     enable : function()
25509     {
25510         if(this.inputType != 'radio'){
25511             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25512             return;
25513         }
25514         
25515         var _this = this;
25516         
25517         if(this.rendered){
25518             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25519                 _this.getActionEl().removeClass(this.disabledClass);
25520                 e.dom.disabled = false;
25521             });
25522         }
25523         
25524         this.disabled = false;
25525         this.fireEvent("enable", this);
25526         return this;
25527     },
25528     
25529     setBoxLabel : function(v)
25530     {
25531         this.boxLabel = v;
25532         
25533         if(this.rendered){
25534             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25535         }
25536     }
25537
25538 });
25539
25540 Roo.apply(Roo.bootstrap.form.CheckBox, {
25541     
25542     groups: {},
25543     
25544      /**
25545     * register a CheckBox Group
25546     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25547     */
25548     register : function(checkbox)
25549     {
25550         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25551             this.groups[checkbox.groupId] = {};
25552         }
25553         
25554         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25555             return;
25556         }
25557         
25558         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25559         
25560     },
25561     /**
25562     * fetch a CheckBox Group based on the group ID
25563     * @param {string} the group ID
25564     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25565     */
25566     get: function(groupId) {
25567         if (typeof(this.groups[groupId]) == 'undefined') {
25568             return false;
25569         }
25570         
25571         return this.groups[groupId] ;
25572     }
25573     
25574     
25575 });
25576 /*
25577  * - LGPL
25578  *
25579  * RadioItem
25580  * 
25581  */
25582
25583 /**
25584  * @class Roo.bootstrap.form.Radio
25585  * @extends Roo.bootstrap.Component
25586  * Bootstrap Radio class
25587  * @cfg {String} boxLabel - the label associated
25588  * @cfg {String} value - the value of radio
25589  * 
25590  * @constructor
25591  * Create a new Radio
25592  * @param {Object} config The config object
25593  */
25594 Roo.bootstrap.form.Radio = function(config){
25595     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25596     
25597 };
25598
25599 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25600     
25601     boxLabel : '',
25602     
25603     value : '',
25604     
25605     getAutoCreate : function()
25606     {
25607         var cfg = {
25608             tag : 'div',
25609             cls : 'form-group radio',
25610             cn : [
25611                 {
25612                     tag : 'label',
25613                     cls : 'box-label',
25614                     html : this.boxLabel
25615                 }
25616             ]
25617         };
25618         
25619         return cfg;
25620     },
25621     
25622     initEvents : function() 
25623     {
25624         this.parent().register(this);
25625         
25626         this.el.on('click', this.onClick, this);
25627         
25628     },
25629     
25630     onClick : function(e)
25631     {
25632         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25633             this.setChecked(true);
25634         }
25635     },
25636     
25637     setChecked : function(state, suppressEvent)
25638     {
25639         this.parent().setValue(this.value, suppressEvent);
25640         
25641     },
25642     
25643     setBoxLabel : function(v)
25644     {
25645         this.boxLabel = v;
25646         
25647         if(this.rendered){
25648             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25649         }
25650     }
25651     
25652 });
25653  
25654
25655  /*
25656  * - LGPL
25657  *
25658  * Input
25659  * 
25660  */
25661
25662 /**
25663  * @class Roo.bootstrap.form.SecurePass
25664  * @extends Roo.bootstrap.form.Input
25665  * Bootstrap SecurePass class
25666  *
25667  * 
25668  * @constructor
25669  * Create a new SecurePass
25670  * @param {Object} config The config object
25671  */
25672  
25673 Roo.bootstrap.form.SecurePass = function (config) {
25674     // these go here, so the translation tool can replace them..
25675     this.errors = {
25676         PwdEmpty: "Please type a password, and then retype it to confirm.",
25677         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25678         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25679         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25680         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25681         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25682         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25683         TooWeak: "Your password is Too Weak."
25684     },
25685     this.meterLabel = "Password strength:";
25686     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25687     this.meterClass = [
25688         "roo-password-meter-tooweak", 
25689         "roo-password-meter-weak", 
25690         "roo-password-meter-medium", 
25691         "roo-password-meter-strong", 
25692         "roo-password-meter-grey"
25693     ];
25694     
25695     this.errors = {};
25696     
25697     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25698 }
25699
25700 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25701     /**
25702      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25703      * {
25704      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25705      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25706      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25707      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25708      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25709      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25710      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25711      * })
25712      */
25713     // private
25714     
25715     meterWidth: 300,
25716     errorMsg :'',    
25717     errors: false,
25718     imageRoot: '/',
25719     /**
25720      * @cfg {String/Object} Label for the strength meter (defaults to
25721      * 'Password strength:')
25722      */
25723     // private
25724     meterLabel: '',
25725     /**
25726      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25727      * ['Weak', 'Medium', 'Strong'])
25728      */
25729     // private    
25730     pwdStrengths: false,    
25731     // private
25732     strength: 0,
25733     // private
25734     _lastPwd: null,
25735     // private
25736     kCapitalLetter: 0,
25737     kSmallLetter: 1,
25738     kDigit: 2,
25739     kPunctuation: 3,
25740     
25741     insecure: false,
25742     // private
25743     initEvents: function ()
25744     {
25745         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25746
25747         if (this.el.is('input[type=password]') && Roo.isSafari) {
25748             this.el.on('keydown', this.SafariOnKeyDown, this);
25749         }
25750
25751         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25752     },
25753     // private
25754     onRender: function (ct, position)
25755     {
25756         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25757         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25758         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25759
25760         this.trigger.createChild({
25761                    cn: [
25762                     {
25763                     //id: 'PwdMeter',
25764                     tag: 'div',
25765                     cls: 'roo-password-meter-grey col-xs-12',
25766                     style: {
25767                         //width: 0,
25768                         //width: this.meterWidth + 'px'                                                
25769                         }
25770                     },
25771                     {                            
25772                          cls: 'roo-password-meter-text'                          
25773                     }
25774                 ]            
25775         });
25776
25777          
25778         if (this.hideTrigger) {
25779             this.trigger.setDisplayed(false);
25780         }
25781         this.setSize(this.width || '', this.height || '');
25782     },
25783     // private
25784     onDestroy: function ()
25785     {
25786         if (this.trigger) {
25787             this.trigger.removeAllListeners();
25788             this.trigger.remove();
25789         }
25790         if (this.wrap) {
25791             this.wrap.remove();
25792         }
25793         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25794     },
25795     // private
25796     checkStrength: function ()
25797     {
25798         var pwd = this.inputEl().getValue();
25799         if (pwd == this._lastPwd) {
25800             return;
25801         }
25802
25803         var strength;
25804         if (this.ClientSideStrongPassword(pwd)) {
25805             strength = 3;
25806         } else if (this.ClientSideMediumPassword(pwd)) {
25807             strength = 2;
25808         } else if (this.ClientSideWeakPassword(pwd)) {
25809             strength = 1;
25810         } else {
25811             strength = 0;
25812         }
25813         
25814         Roo.log('strength1: ' + strength);
25815         
25816         //var pm = this.trigger.child('div/div/div').dom;
25817         var pm = this.trigger.child('div/div');
25818         pm.removeClass(this.meterClass);
25819         pm.addClass(this.meterClass[strength]);
25820                 
25821         
25822         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25823                 
25824         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25825         
25826         this._lastPwd = pwd;
25827     },
25828     reset: function ()
25829     {
25830         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25831         
25832         this._lastPwd = '';
25833         
25834         var pm = this.trigger.child('div/div');
25835         pm.removeClass(this.meterClass);
25836         pm.addClass('roo-password-meter-grey');        
25837         
25838         
25839         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25840         
25841         pt.innerHTML = '';
25842         this.inputEl().dom.type='password';
25843     },
25844     // private
25845     validateValue: function (value)
25846     {
25847         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25848             return false;
25849         }
25850         if (value.length == 0) {
25851             if (this.allowBlank) {
25852                 this.clearInvalid();
25853                 return true;
25854             }
25855
25856             this.markInvalid(this.errors.PwdEmpty);
25857             this.errorMsg = this.errors.PwdEmpty;
25858             return false;
25859         }
25860         
25861         if(this.insecure){
25862             return true;
25863         }
25864         
25865         if (!value.match(/[\x21-\x7e]+/)) {
25866             this.markInvalid(this.errors.PwdBadChar);
25867             this.errorMsg = this.errors.PwdBadChar;
25868             return false;
25869         }
25870         if (value.length < 6) {
25871             this.markInvalid(this.errors.PwdShort);
25872             this.errorMsg = this.errors.PwdShort;
25873             return false;
25874         }
25875         if (value.length > 16) {
25876             this.markInvalid(this.errors.PwdLong);
25877             this.errorMsg = this.errors.PwdLong;
25878             return false;
25879         }
25880         var strength;
25881         if (this.ClientSideStrongPassword(value)) {
25882             strength = 3;
25883         } else if (this.ClientSideMediumPassword(value)) {
25884             strength = 2;
25885         } else if (this.ClientSideWeakPassword(value)) {
25886             strength = 1;
25887         } else {
25888             strength = 0;
25889         }
25890
25891         
25892         if (strength < 2) {
25893             //this.markInvalid(this.errors.TooWeak);
25894             this.errorMsg = this.errors.TooWeak;
25895             //return false;
25896         }
25897         
25898         
25899         console.log('strength2: ' + strength);
25900         
25901         //var pm = this.trigger.child('div/div/div').dom;
25902         
25903         var pm = this.trigger.child('div/div');
25904         pm.removeClass(this.meterClass);
25905         pm.addClass(this.meterClass[strength]);
25906                 
25907         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25908                 
25909         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25910         
25911         this.errorMsg = ''; 
25912         return true;
25913     },
25914     // private
25915     CharacterSetChecks: function (type)
25916     {
25917         this.type = type;
25918         this.fResult = false;
25919     },
25920     // private
25921     isctype: function (character, type)
25922     {
25923         switch (type) {  
25924             case this.kCapitalLetter:
25925                 if (character >= 'A' && character <= 'Z') {
25926                     return true;
25927                 }
25928                 break;
25929             
25930             case this.kSmallLetter:
25931                 if (character >= 'a' && character <= 'z') {
25932                     return true;
25933                 }
25934                 break;
25935             
25936             case this.kDigit:
25937                 if (character >= '0' && character <= '9') {
25938                     return true;
25939                 }
25940                 break;
25941             
25942             case this.kPunctuation:
25943                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25944                     return true;
25945                 }
25946                 break;
25947             
25948             default:
25949                 return false;
25950         }
25951
25952     },
25953     // private
25954     IsLongEnough: function (pwd, size)
25955     {
25956         return !(pwd == null || isNaN(size) || pwd.length < size);
25957     },
25958     // private
25959     SpansEnoughCharacterSets: function (word, nb)
25960     {
25961         if (!this.IsLongEnough(word, nb))
25962         {
25963             return false;
25964         }
25965
25966         var characterSetChecks = new Array(
25967             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25968             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25969         );
25970         
25971         for (var index = 0; index < word.length; ++index) {
25972             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25973                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25974                     characterSetChecks[nCharSet].fResult = true;
25975                     break;
25976                 }
25977             }
25978         }
25979
25980         var nCharSets = 0;
25981         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25982             if (characterSetChecks[nCharSet].fResult) {
25983                 ++nCharSets;
25984             }
25985         }
25986
25987         if (nCharSets < nb) {
25988             return false;
25989         }
25990         return true;
25991     },
25992     // private
25993     ClientSideStrongPassword: function (pwd)
25994     {
25995         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25996     },
25997     // private
25998     ClientSideMediumPassword: function (pwd)
25999     {
26000         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26001     },
26002     // private
26003     ClientSideWeakPassword: function (pwd)
26004     {
26005         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26006     }
26007           
26008 });Roo.rtf = {}; // namespace
26009 Roo.rtf.Hex = function(hex)
26010 {
26011     this.hexstr = hex;
26012 };
26013 Roo.rtf.Paragraph = function(opts)
26014 {
26015     this.content = []; ///??? is that used?
26016 };Roo.rtf.Span = function(opts)
26017 {
26018     this.value = opts.value;
26019 };
26020
26021 Roo.rtf.Group = function(parent)
26022 {
26023     // we dont want to acutally store parent - it will make debug a nightmare..
26024     this.content = [];
26025     this.cn  = [];
26026      
26027        
26028     
26029 };
26030
26031 Roo.rtf.Group.prototype = {
26032     ignorable : false,
26033     content: false,
26034     cn: false,
26035     addContent : function(node) {
26036         // could set styles...
26037         this.content.push(node);
26038     },
26039     addChild : function(cn)
26040     {
26041         this.cn.push(cn);
26042     },
26043     // only for images really...
26044     toDataURL : function()
26045     {
26046         var mimetype = false;
26047         switch(true) {
26048             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26049                 mimetype = "image/png";
26050                 break;
26051              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26052                 mimetype = "image/jpeg";
26053                 break;
26054             default :
26055                 return 'about:blank'; // ?? error?
26056         }
26057         
26058         
26059         var hexstring = this.content[this.content.length-1].value;
26060         
26061         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26062             return String.fromCharCode(parseInt(a, 16));
26063         }).join(""));
26064     }
26065     
26066 };
26067 // this looks like it's normally the {rtf{ .... }}
26068 Roo.rtf.Document = function()
26069 {
26070     // we dont want to acutally store parent - it will make debug a nightmare..
26071     this.rtlch  = [];
26072     this.content = [];
26073     this.cn = [];
26074     
26075 };
26076 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26077     addChild : function(cn)
26078     {
26079         this.cn.push(cn);
26080         switch(cn.type) {
26081             case 'rtlch': // most content seems to be inside this??
26082             case 'listtext':
26083             case 'shpinst':
26084                 this.rtlch.push(cn);
26085                 return;
26086             default:
26087                 this[cn.type] = cn;
26088         }
26089         
26090     },
26091     
26092     getElementsByType : function(type)
26093     {
26094         var ret =  [];
26095         this._getElementsByType(type, ret, this.cn, 'rtf');
26096         return ret;
26097     },
26098     _getElementsByType : function (type, ret, search_array, path)
26099     {
26100         search_array.forEach(function(n,i) {
26101             if (n.type == type) {
26102                 n.path = path + '/' + n.type + ':' + i;
26103                 ret.push(n);
26104             }
26105             if (n.cn.length > 0) {
26106                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26107             }
26108         },this);
26109     }
26110     
26111 });
26112  
26113 Roo.rtf.Ctrl = function(opts)
26114 {
26115     this.value = opts.value;
26116     this.param = opts.param;
26117 };
26118 /**
26119  *
26120  *
26121  * based on this https://github.com/iarna/rtf-parser
26122  * it's really only designed to extract pict from pasted RTF 
26123  *
26124  * usage:
26125  *
26126  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26127  *  
26128  *
26129  */
26130
26131  
26132
26133
26134
26135 Roo.rtf.Parser = function(text) {
26136     //super({objectMode: true})
26137     this.text = '';
26138     this.parserState = this.parseText;
26139     
26140     // these are for interpeter...
26141     this.doc = {};
26142     ///this.parserState = this.parseTop
26143     this.groupStack = [];
26144     this.hexStore = [];
26145     this.doc = false;
26146     
26147     this.groups = []; // where we put the return.
26148     
26149     for (var ii = 0; ii < text.length; ++ii) {
26150         ++this.cpos;
26151         
26152         if (text[ii] === '\n') {
26153             ++this.row;
26154             this.col = 1;
26155         } else {
26156             ++this.col;
26157         }
26158         this.parserState(text[ii]);
26159     }
26160     
26161     
26162     
26163 };
26164 Roo.rtf.Parser.prototype = {
26165     text : '', // string being parsed..
26166     controlWord : '',
26167     controlWordParam :  '',
26168     hexChar : '',
26169     doc : false,
26170     group: false,
26171     groupStack : false,
26172     hexStore : false,
26173     
26174     
26175     cpos : 0, 
26176     row : 1, // reportin?
26177     col : 1, //
26178
26179      
26180     push : function (el)
26181     {
26182         var m = 'cmd'+ el.type;
26183         if (typeof(this[m]) == 'undefined') {
26184             Roo.log('invalid cmd:' + el.type);
26185             return;
26186         }
26187         this[m](el);
26188         //Roo.log(el);
26189     },
26190     flushHexStore : function()
26191     {
26192         if (this.hexStore.length < 1) {
26193             return;
26194         }
26195         var hexstr = this.hexStore.map(
26196             function(cmd) {
26197                 return cmd.value;
26198         }).join('');
26199         
26200         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26201               
26202             
26203         this.hexStore.splice(0)
26204         
26205     },
26206     
26207     cmdgroupstart : function()
26208     {
26209         this.flushHexStore();
26210         if (this.group) {
26211             this.groupStack.push(this.group);
26212         }
26213          // parent..
26214         if (this.doc === false) {
26215             this.group = this.doc = new Roo.rtf.Document();
26216             return;
26217             
26218         }
26219         this.group = new Roo.rtf.Group(this.group);
26220     },
26221     cmdignorable : function()
26222     {
26223         this.flushHexStore();
26224         this.group.ignorable = true;
26225     },
26226     cmdendparagraph : function()
26227     {
26228         this.flushHexStore();
26229         this.group.addContent(new Roo.rtf.Paragraph());
26230     },
26231     cmdgroupend : function ()
26232     {
26233         this.flushHexStore();
26234         var endingGroup = this.group;
26235         
26236         
26237         this.group = this.groupStack.pop();
26238         if (this.group) {
26239             this.group.addChild(endingGroup);
26240         }
26241         
26242         
26243         
26244         var doc = this.group || this.doc;
26245         //if (endingGroup instanceof FontTable) {
26246         //  doc.fonts = endingGroup.table
26247         //} else if (endingGroup instanceof ColorTable) {
26248         //  doc.colors = endingGroup.table
26249         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26250         if (endingGroup.ignorable === false) {
26251             //code
26252             this.groups.push(endingGroup);
26253            // Roo.log( endingGroup );
26254         }
26255             //Roo.each(endingGroup.content, function(item)) {
26256             //    doc.addContent(item);
26257             //}
26258             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26259         //}
26260     },
26261     cmdtext : function (cmd)
26262     {
26263         this.flushHexStore();
26264         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26265             //this.group = this.doc
26266             return;  // we really don't care about stray text...
26267         }
26268         this.group.addContent(new Roo.rtf.Span(cmd));
26269     },
26270     cmdcontrolword : function (cmd)
26271     {
26272         this.flushHexStore();
26273         if (!this.group.type) {
26274             this.group.type = cmd.value;
26275             return;
26276         }
26277         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26278         // we actually don't care about ctrl words...
26279         return ;
26280         /*
26281         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26282         if (this[method]) {
26283             this[method](cmd.param)
26284         } else {
26285             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26286         }
26287         */
26288     },
26289     cmdhexchar : function(cmd) {
26290         this.hexStore.push(cmd);
26291     },
26292     cmderror : function(cmd) {
26293         throw cmd.value;
26294     },
26295     
26296     /*
26297       _flush (done) {
26298         if (this.text !== '\u0000') this.emitText()
26299         done()
26300       }
26301       */
26302       
26303       
26304     parseText : function(c)
26305     {
26306         if (c === '\\') {
26307             this.parserState = this.parseEscapes;
26308         } else if (c === '{') {
26309             this.emitStartGroup();
26310         } else if (c === '}') {
26311             this.emitEndGroup();
26312         } else if (c === '\x0A' || c === '\x0D') {
26313             // cr/lf are noise chars
26314         } else {
26315             this.text += c;
26316         }
26317     },
26318     
26319     parseEscapes: function (c)
26320     {
26321         if (c === '\\' || c === '{' || c === '}') {
26322             this.text += c;
26323             this.parserState = this.parseText;
26324         } else {
26325             this.parserState = this.parseControlSymbol;
26326             this.parseControlSymbol(c);
26327         }
26328     },
26329     parseControlSymbol: function(c)
26330     {
26331         if (c === '~') {
26332             this.text += '\u00a0'; // nbsp
26333             this.parserState = this.parseText
26334         } else if (c === '-') {
26335              this.text += '\u00ad'; // soft hyphen
26336         } else if (c === '_') {
26337             this.text += '\u2011'; // non-breaking hyphen
26338         } else if (c === '*') {
26339             this.emitIgnorable();
26340             this.parserState = this.parseText;
26341         } else if (c === "'") {
26342             this.parserState = this.parseHexChar;
26343         } else if (c === '|') { // formula cacter
26344             this.emitFormula();
26345             this.parserState = this.parseText;
26346         } else if (c === ':') { // subentry in an index entry
26347             this.emitIndexSubEntry();
26348             this.parserState = this.parseText;
26349         } else if (c === '\x0a') {
26350             this.emitEndParagraph();
26351             this.parserState = this.parseText;
26352         } else if (c === '\x0d') {
26353             this.emitEndParagraph();
26354             this.parserState = this.parseText;
26355         } else {
26356             this.parserState = this.parseControlWord;
26357             this.parseControlWord(c);
26358         }
26359     },
26360     parseHexChar: function (c)
26361     {
26362         if (/^[A-Fa-f0-9]$/.test(c)) {
26363             this.hexChar += c;
26364             if (this.hexChar.length >= 2) {
26365               this.emitHexChar();
26366               this.parserState = this.parseText;
26367             }
26368             return;
26369         }
26370         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26371         this.parserState = this.parseText;
26372         
26373     },
26374     parseControlWord : function(c)
26375     {
26376         if (c === ' ') {
26377             this.emitControlWord();
26378             this.parserState = this.parseText;
26379         } else if (/^[-\d]$/.test(c)) {
26380             this.parserState = this.parseControlWordParam;
26381             this.controlWordParam += c;
26382         } else if (/^[A-Za-z]$/.test(c)) {
26383           this.controlWord += c;
26384         } else {
26385           this.emitControlWord();
26386           this.parserState = this.parseText;
26387           this.parseText(c);
26388         }
26389     },
26390     parseControlWordParam : function (c) {
26391         if (/^\d$/.test(c)) {
26392           this.controlWordParam += c;
26393         } else if (c === ' ') {
26394           this.emitControlWord();
26395           this.parserState = this.parseText;
26396         } else {
26397           this.emitControlWord();
26398           this.parserState = this.parseText;
26399           this.parseText(c);
26400         }
26401     },
26402     
26403     
26404     
26405     
26406     emitText : function () {
26407         if (this.text === '') {
26408             return;
26409         }
26410         this.push({
26411             type: 'text',
26412             value: this.text,
26413             pos: this.cpos,
26414             row: this.row,
26415             col: this.col
26416         });
26417         this.text = ''
26418     },
26419     emitControlWord : function ()
26420     {
26421         this.emitText();
26422         if (this.controlWord === '') {
26423             // do we want to track this - it seems just to cause problems.
26424             //this.emitError('empty control word');
26425         } else {
26426             this.push({
26427                   type: 'controlword',
26428                   value: this.controlWord,
26429                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26430                   pos: this.cpos,
26431                   row: this.row,
26432                   col: this.col
26433             });
26434         }
26435         this.controlWord = '';
26436         this.controlWordParam = '';
26437     },
26438     emitStartGroup : function ()
26439     {
26440         this.emitText();
26441         this.push({
26442             type: 'groupstart',
26443             pos: this.cpos,
26444             row: this.row,
26445             col: this.col
26446         });
26447     },
26448     emitEndGroup : function ()
26449     {
26450         this.emitText();
26451         this.push({
26452             type: 'groupend',
26453             pos: this.cpos,
26454             row: this.row,
26455             col: this.col
26456         });
26457     },
26458     emitIgnorable : function ()
26459     {
26460         this.emitText();
26461         this.push({
26462             type: 'ignorable',
26463             pos: this.cpos,
26464             row: this.row,
26465             col: this.col
26466         });
26467     },
26468     emitHexChar : function ()
26469     {
26470         this.emitText();
26471         this.push({
26472             type: 'hexchar',
26473             value: this.hexChar,
26474             pos: this.cpos,
26475             row: this.row,
26476             col: this.col
26477         });
26478         this.hexChar = ''
26479     },
26480     emitError : function (message)
26481     {
26482       this.emitText();
26483       this.push({
26484             type: 'error',
26485             value: message,
26486             row: this.row,
26487             col: this.col,
26488             char: this.cpos //,
26489             //stack: new Error().stack
26490         });
26491     },
26492     emitEndParagraph : function () {
26493         this.emitText();
26494         this.push({
26495             type: 'endparagraph',
26496             pos: this.cpos,
26497             row: this.row,
26498             col: this.col
26499         });
26500     }
26501      
26502 } ; 
26503 /**
26504  * @class Roo.htmleditor.Filter
26505  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26506  * @cfg {DomElement} node The node to iterate and filter
26507  * @cfg {boolean|String|Array} tag Tags to replace 
26508  * @constructor
26509  * Create a new Filter.
26510  * @param {Object} config Configuration options
26511  */
26512
26513
26514
26515 Roo.htmleditor.Filter = function(cfg) {
26516     Roo.apply(this.cfg);
26517     // this does not actually call walk as it's really just a abstract class
26518 }
26519
26520
26521 Roo.htmleditor.Filter.prototype = {
26522     
26523     node: false,
26524     
26525     tag: false,
26526
26527     // overrride to do replace comments.
26528     replaceComment : false,
26529     
26530     // overrride to do replace or do stuff with tags..
26531     replaceTag : false,
26532     
26533     walk : function(dom)
26534     {
26535         Roo.each( Array.from(dom.childNodes), function( e ) {
26536             switch(true) {
26537                 
26538                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26539                     this.replaceComment(e);
26540                     return;
26541                 
26542                 case e.nodeType != 1: //not a node.
26543                     return;
26544                 
26545                 case this.tag === true: // everything
26546                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26547                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26548                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26549                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26550                     if (this.replaceTag && false === this.replaceTag(e)) {
26551                         return;
26552                     }
26553                     if (e.hasChildNodes()) {
26554                         this.walk(e);
26555                     }
26556                     return;
26557                 
26558                 default:    // tags .. that do not match.
26559                     if (e.hasChildNodes()) {
26560                         this.walk(e);
26561                     }
26562             }
26563             
26564         }, this);
26565         
26566     },
26567     
26568     
26569     removeNodeKeepChildren : function( node)
26570     {
26571     
26572         ar = Array.from(node.childNodes);
26573         for (var i = 0; i < ar.length; i++) {
26574          
26575             node.removeChild(ar[i]);
26576             // what if we need to walk these???
26577             node.parentNode.insertBefore(ar[i], node);
26578            
26579         }
26580         node.parentNode.removeChild(node);
26581     }
26582 }; 
26583
26584 /**
26585  * @class Roo.htmleditor.FilterAttributes
26586  * clean attributes and  styles including http:// etc.. in attribute
26587  * @constructor
26588 * Run a new Attribute Filter
26589 * @param {Object} config Configuration options
26590  */
26591 Roo.htmleditor.FilterAttributes = function(cfg)
26592 {
26593     Roo.apply(this, cfg);
26594     this.attrib_black = this.attrib_black || [];
26595     this.attrib_white = this.attrib_white || [];
26596
26597     this.attrib_clean = this.attrib_clean || [];
26598     this.style_white = this.style_white || [];
26599     this.style_black = this.style_black || [];
26600     this.walk(cfg.node);
26601 }
26602
26603 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26604 {
26605     tag: true, // all tags
26606     
26607     attrib_black : false, // array
26608     attrib_clean : false,
26609     attrib_white : false,
26610
26611     style_white : false,
26612     style_black : false,
26613      
26614      
26615     replaceTag : function(node)
26616     {
26617         if (!node.attributes || !node.attributes.length) {
26618             return true;
26619         }
26620         
26621         for (var i = node.attributes.length-1; i > -1 ; i--) {
26622             var a = node.attributes[i];
26623             //console.log(a);
26624             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26625                 node.removeAttribute(a.name);
26626                 continue;
26627             }
26628             
26629             
26630             
26631             if (a.name.toLowerCase().substr(0,2)=='on')  {
26632                 node.removeAttribute(a.name);
26633                 continue;
26634             }
26635             
26636             
26637             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26638                 node.removeAttribute(a.name);
26639                 continue;
26640             }
26641             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26642                 this.cleanAttr(node,a.name,a.value); // fixme..
26643                 continue;
26644             }
26645             if (a.name == 'style') {
26646                 this.cleanStyle(node,a.name,a.value);
26647                 continue;
26648             }
26649             /// clean up MS crap..
26650             // tecnically this should be a list of valid class'es..
26651             
26652             
26653             if (a.name == 'class') {
26654                 if (a.value.match(/^Mso/)) {
26655                     node.removeAttribute('class');
26656                 }
26657                 
26658                 if (a.value.match(/^body$/)) {
26659                     node.removeAttribute('class');
26660                 }
26661                 continue;
26662             }
26663             
26664             
26665             // style cleanup!?
26666             // class cleanup?
26667             
26668         }
26669         return true; // clean children
26670     },
26671         
26672     cleanAttr: function(node, n,v)
26673     {
26674         
26675         if (v.match(/^\./) || v.match(/^\//)) {
26676             return;
26677         }
26678         if (v.match(/^(http|https):\/\//)
26679             || v.match(/^mailto:/) 
26680             || v.match(/^ftp:/)
26681             || v.match(/^data:/)
26682             ) {
26683             return;
26684         }
26685         if (v.match(/^#/)) {
26686             return;
26687         }
26688         if (v.match(/^\{/)) { // allow template editing.
26689             return;
26690         }
26691 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26692         node.removeAttribute(n);
26693         
26694     },
26695     cleanStyle : function(node,  n,v)
26696     {
26697         if (v.match(/expression/)) { //XSS?? should we even bother..
26698             node.removeAttribute(n);
26699             return;
26700         }
26701         
26702         var parts = v.split(/;/);
26703         var clean = [];
26704         
26705         Roo.each(parts, function(p) {
26706             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26707             if (!p.length) {
26708                 return true;
26709             }
26710             var l = p.split(':').shift().replace(/\s+/g,'');
26711             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26712             
26713             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26714                 return true;
26715             }
26716             //Roo.log()
26717             // only allow 'c whitelisted system attributes'
26718             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26719                 return true;
26720             }
26721             
26722             
26723             clean.push(p);
26724             return true;
26725         },this);
26726         if (clean.length) { 
26727             node.setAttribute(n, clean.join(';'));
26728         } else {
26729             node.removeAttribute(n);
26730         }
26731         
26732     }
26733         
26734         
26735         
26736     
26737 });/**
26738  * @class Roo.htmleditor.FilterBlack
26739  * remove blacklisted elements.
26740  * @constructor
26741  * Run a new Blacklisted Filter
26742  * @param {Object} config Configuration options
26743  */
26744
26745 Roo.htmleditor.FilterBlack = function(cfg)
26746 {
26747     Roo.apply(this, cfg);
26748     this.walk(cfg.node);
26749 }
26750
26751 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26752 {
26753     tag : true, // all elements.
26754    
26755     replaceTag : function(n)
26756     {
26757         n.parentNode.removeChild(n);
26758     }
26759 });
26760 /**
26761  * @class Roo.htmleditor.FilterComment
26762  * remove comments.
26763  * @constructor
26764 * Run a new Comments Filter
26765 * @param {Object} config Configuration options
26766  */
26767 Roo.htmleditor.FilterComment = function(cfg)
26768 {
26769     this.walk(cfg.node);
26770 }
26771
26772 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26773 {
26774   
26775     replaceComment : function(n)
26776     {
26777         n.parentNode.removeChild(n);
26778     }
26779 });/**
26780  * @class Roo.htmleditor.FilterKeepChildren
26781  * remove tags but keep children
26782  * @constructor
26783  * Run a new Keep Children Filter
26784  * @param {Object} config Configuration options
26785  */
26786
26787 Roo.htmleditor.FilterKeepChildren = function(cfg)
26788 {
26789     Roo.apply(this, cfg);
26790     if (this.tag === false) {
26791         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26792     }
26793     // hacky?
26794     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26795         this.cleanNamespace = true;
26796     }
26797         
26798     this.walk(cfg.node);
26799 }
26800
26801 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26802 {
26803     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26804   
26805     replaceTag : function(node)
26806     {
26807         // walk children...
26808         //Roo.log(node.tagName);
26809         var ar = Array.from(node.childNodes);
26810         //remove first..
26811         
26812         for (var i = 0; i < ar.length; i++) {
26813             var e = ar[i];
26814             if (e.nodeType == 1) {
26815                 if (
26816                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26817                     || // array and it matches
26818                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26819                     ||
26820                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26821                     ||
26822                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26823                 ) {
26824                     this.replaceTag(ar[i]); // child is blacklisted as well...
26825                     continue;
26826                 }
26827             }
26828         }  
26829         ar = Array.from(node.childNodes);
26830         for (var i = 0; i < ar.length; i++) {
26831          
26832             node.removeChild(ar[i]);
26833             // what if we need to walk these???
26834             node.parentNode.insertBefore(ar[i], node);
26835             if (this.tag !== false) {
26836                 this.walk(ar[i]);
26837                 
26838             }
26839         }
26840         //Roo.log("REMOVE:" + node.tagName);
26841         node.parentNode.removeChild(node);
26842         return false; // don't walk children
26843         
26844         
26845     }
26846 });/**
26847  * @class Roo.htmleditor.FilterParagraph
26848  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26849  * like on 'push' to remove the <p> tags and replace them with line breaks.
26850  * @constructor
26851  * Run a new Paragraph Filter
26852  * @param {Object} config Configuration options
26853  */
26854
26855 Roo.htmleditor.FilterParagraph = function(cfg)
26856 {
26857     // no need to apply config.
26858     this.walk(cfg.node);
26859 }
26860
26861 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26862 {
26863     
26864      
26865     tag : 'P',
26866     
26867      
26868     replaceTag : function(node)
26869     {
26870         
26871         if (node.childNodes.length == 1 &&
26872             node.childNodes[0].nodeType == 3 &&
26873             node.childNodes[0].textContent.trim().length < 1
26874             ) {
26875             // remove and replace with '<BR>';
26876             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26877             return false; // no need to walk..
26878         }
26879         var ar = Array.from(node.childNodes);
26880         for (var i = 0; i < ar.length; i++) {
26881             node.removeChild(ar[i]);
26882             // what if we need to walk these???
26883             node.parentNode.insertBefore(ar[i], node);
26884         }
26885         // now what about this?
26886         // <p> &nbsp; </p>
26887         
26888         // double BR.
26889         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26890         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26891         node.parentNode.removeChild(node);
26892         
26893         return false;
26894
26895     }
26896     
26897 });/**
26898  * @class Roo.htmleditor.FilterSpan
26899  * filter span's with no attributes out..
26900  * @constructor
26901  * Run a new Span Filter
26902  * @param {Object} config Configuration options
26903  */
26904
26905 Roo.htmleditor.FilterSpan = function(cfg)
26906 {
26907     // no need to apply config.
26908     this.walk(cfg.node);
26909 }
26910
26911 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26912 {
26913      
26914     tag : 'SPAN',
26915      
26916  
26917     replaceTag : function(node)
26918     {
26919         if (node.attributes && node.attributes.length > 0) {
26920             return true; // walk if there are any.
26921         }
26922         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26923         return false;
26924      
26925     }
26926     
26927 });/**
26928  * @class Roo.htmleditor.FilterTableWidth
26929   try and remove table width data - as that frequently messes up other stuff.
26930  * 
26931  *      was cleanTableWidths.
26932  *
26933  * Quite often pasting from word etc.. results in tables with column and widths.
26934  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26935  *
26936  * @constructor
26937  * Run a new Table Filter
26938  * @param {Object} config Configuration options
26939  */
26940
26941 Roo.htmleditor.FilterTableWidth = function(cfg)
26942 {
26943     // no need to apply config.
26944     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26945     this.walk(cfg.node);
26946 }
26947
26948 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26949 {
26950      
26951      
26952     
26953     replaceTag: function(node) {
26954         
26955         
26956       
26957         if (node.hasAttribute('width')) {
26958             node.removeAttribute('width');
26959         }
26960         
26961          
26962         if (node.hasAttribute("style")) {
26963             // pretty basic...
26964             
26965             var styles = node.getAttribute("style").split(";");
26966             var nstyle = [];
26967             Roo.each(styles, function(s) {
26968                 if (!s.match(/:/)) {
26969                     return;
26970                 }
26971                 var kv = s.split(":");
26972                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26973                     return;
26974                 }
26975                 // what ever is left... we allow.
26976                 nstyle.push(s);
26977             });
26978             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26979             if (!nstyle.length) {
26980                 node.removeAttribute('style');
26981             }
26982         }
26983         
26984         return true; // continue doing children..
26985     }
26986 });/**
26987  * @class Roo.htmleditor.FilterWord
26988  * try and clean up all the mess that Word generates.
26989  * 
26990  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26991  
26992  * @constructor
26993  * Run a new Span Filter
26994  * @param {Object} config Configuration options
26995  */
26996
26997 Roo.htmleditor.FilterWord = function(cfg)
26998 {
26999     // no need to apply config.
27000     this.replaceDocBullets(cfg.node);
27001     
27002     this.replaceAname(cfg.node);
27003     // this is disabled as the removal is done by other filters;
27004    // this.walk(cfg.node);
27005     this.replaceImageTable(cfg.node);
27006     
27007 }
27008
27009 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27010 {
27011     tag: true,
27012      
27013     
27014     /**
27015      * Clean up MS wordisms...
27016      */
27017     replaceTag : function(node)
27018     {
27019          
27020         // no idea what this does - span with text, replaceds with just text.
27021         if(
27022                 node.nodeName == 'SPAN' &&
27023                 !node.hasAttributes() &&
27024                 node.childNodes.length == 1 &&
27025                 node.firstChild.nodeName == "#text"  
27026         ) {
27027             var textNode = node.firstChild;
27028             node.removeChild(textNode);
27029             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27030                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27031             }
27032             node.parentNode.insertBefore(textNode, node);
27033             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27034                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27035             }
27036             
27037             node.parentNode.removeChild(node);
27038             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27039         }
27040         
27041    
27042         
27043         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27044             node.parentNode.removeChild(node);
27045             return false; // dont do chidlren
27046         }
27047         //Roo.log(node.tagName);
27048         // remove - but keep children..
27049         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27050             //Roo.log('-- removed');
27051             while (node.childNodes.length) {
27052                 var cn = node.childNodes[0];
27053                 node.removeChild(cn);
27054                 node.parentNode.insertBefore(cn, node);
27055                 // move node to parent - and clean it..
27056                 if (cn.nodeType == 1) {
27057                     this.replaceTag(cn);
27058                 }
27059                 
27060             }
27061             node.parentNode.removeChild(node);
27062             /// no need to iterate chidlren = it's got none..
27063             //this.iterateChildren(node, this.cleanWord);
27064             return false; // no need to iterate children.
27065         }
27066         // clean styles
27067         if (node.className.length) {
27068             
27069             var cn = node.className.split(/\W+/);
27070             var cna = [];
27071             Roo.each(cn, function(cls) {
27072                 if (cls.match(/Mso[a-zA-Z]+/)) {
27073                     return;
27074                 }
27075                 cna.push(cls);
27076             });
27077             node.className = cna.length ? cna.join(' ') : '';
27078             if (!cna.length) {
27079                 node.removeAttribute("class");
27080             }
27081         }
27082         
27083         if (node.hasAttribute("lang")) {
27084             node.removeAttribute("lang");
27085         }
27086         
27087         if (node.hasAttribute("style")) {
27088             
27089             var styles = node.getAttribute("style").split(";");
27090             var nstyle = [];
27091             Roo.each(styles, function(s) {
27092                 if (!s.match(/:/)) {
27093                     return;
27094                 }
27095                 var kv = s.split(":");
27096                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27097                     return;
27098                 }
27099                 // what ever is left... we allow.
27100                 nstyle.push(s);
27101             });
27102             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27103             if (!nstyle.length) {
27104                 node.removeAttribute('style');
27105             }
27106         }
27107         return true; // do children
27108         
27109         
27110         
27111     },
27112     
27113     styleToObject: function(node)
27114     {
27115         var styles = (node.getAttribute("style") || '').split(";");
27116         var ret = {};
27117         Roo.each(styles, function(s) {
27118             if (!s.match(/:/)) {
27119                 return;
27120             }
27121             var kv = s.split(":");
27122              
27123             // what ever is left... we allow.
27124             ret[kv[0].trim()] = kv[1];
27125         });
27126         return ret;
27127     },
27128     
27129     
27130     replaceAname : function (doc)
27131     {
27132         // replace all the a/name without..
27133         var aa = Array.from(doc.getElementsByTagName('a'));
27134         for (var i = 0; i  < aa.length; i++) {
27135             var a = aa[i];
27136             if (a.hasAttribute("name")) {
27137                 a.removeAttribute("name");
27138             }
27139             if (a.hasAttribute("href")) {
27140                 continue;
27141             }
27142             // reparent children.
27143             this.removeNodeKeepChildren(a);
27144             
27145         }
27146         
27147         
27148         
27149     },
27150
27151     
27152     
27153     replaceDocBullets : function(doc)
27154     {
27155         // this is a bit odd - but it appears some indents use ql-indent-1
27156          //Roo.log(doc.innerHTML);
27157         
27158         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27159         for( var i = 0; i < listpara.length; i ++) {
27160             listpara[i].className = "MsoListParagraph";
27161         }
27162         
27163         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27164         for( var i = 0; i < listpara.length; i ++) {
27165             listpara[i].className = "MsoListParagraph";
27166         }
27167         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27168         for( var i = 0; i < listpara.length; i ++) {
27169             listpara[i].className = "MsoListParagraph";
27170         }
27171         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27172         for( var i = 0; i < listpara.length; i ++) {
27173             listpara[i].className = "MsoListParagraph";
27174         }
27175         
27176         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27177         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27178         for( var i = 0; i < htwo.length; i ++) {
27179             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27180                 htwo[i].className = "MsoListParagraph";
27181             }
27182         }
27183         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27184         for( var i = 0; i < listpara.length; i ++) {
27185             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27186                 listpara[i].className = "MsoListParagraph";
27187             } else {
27188                 listpara[i].className = "MsoNormalx";
27189             }
27190         }
27191        
27192         listpara = doc.getElementsByClassName('MsoListParagraph');
27193         // Roo.log(doc.innerHTML);
27194         
27195         
27196         
27197         while(listpara.length) {
27198             
27199             this.replaceDocBullet(listpara.item(0));
27200         }
27201       
27202     },
27203     
27204      
27205     
27206     replaceDocBullet : function(p)
27207     {
27208         // gather all the siblings.
27209         var ns = p,
27210             parent = p.parentNode,
27211             doc = parent.ownerDocument,
27212             items = [];
27213          
27214         //Roo.log("Parsing: " + p.innerText)    ;
27215         var listtype = 'ul';   
27216         while (ns) {
27217             if (ns.nodeType != 1) {
27218                 ns = ns.nextSibling;
27219                 continue;
27220             }
27221             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27222                 //Roo.log("Missing para r q1indent - got:" + ns.className);
27223                 break;
27224             }
27225             var spans = ns.getElementsByTagName('span');
27226             
27227             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27228                 items.push(ns);
27229                 ns = ns.nextSibling;
27230                 has_list = true;
27231                 if (!spans.length) {
27232                     continue;
27233                 }
27234                 var ff = '';
27235                 var se = spans[0];
27236                 for (var i = 0; i < spans.length;i++) {
27237                     se = spans[i];
27238                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
27239                         ff = se.style.fontFamily;
27240                         break;
27241                     }
27242                 }
27243                  
27244                     
27245                 //Roo.log("got font family: " + ff);
27246                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27247                     listtype = 'ol';
27248                 }
27249                 
27250                 continue;
27251             }
27252             //Roo.log("no mso-list?");
27253             
27254             var spans = ns.getElementsByTagName('span');
27255             if (!spans.length) {
27256                 break;
27257             }
27258             var has_list  = false;
27259             for(var i = 0; i < spans.length; i++) {
27260                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27261                     has_list = true;
27262                     break;
27263                 }
27264             }
27265             if (!has_list) {
27266                 break;
27267             }
27268             items.push(ns);
27269             ns = ns.nextSibling;
27270             
27271             
27272         }
27273         if (!items.length) {
27274             ns.className = "";
27275             return;
27276         }
27277         
27278         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27279         parent.insertBefore(ul, p);
27280         var lvl = 0;
27281         var stack = [ ul ];
27282         var last_li = false;
27283         
27284         var margin_to_depth = {};
27285         max_margins = -1;
27286         
27287         items.forEach(function(n, ipos) {
27288             //Roo.log("got innertHMLT=" + n.innerHTML);
27289             
27290             var spans = n.getElementsByTagName('span');
27291             if (!spans.length) {
27292                 //Roo.log("No spans found");
27293                  
27294                 parent.removeChild(n);
27295                 
27296                 
27297                 return; // skip it...
27298             }
27299            
27300                 
27301             var num = 1;
27302             var style = {};
27303             for(var i = 0; i < spans.length; i++) {
27304             
27305                 style = this.styleToObject(spans[i]);
27306                 if (typeof(style['mso-list']) == 'undefined') {
27307                     continue;
27308                 }
27309                 if (listtype == 'ol') {
27310                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27311                 }
27312                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27313                 break;
27314             }
27315             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27316             style = this.styleToObject(n); // mo-list is from the parent node.
27317             if (typeof(style['mso-list']) == 'undefined') {
27318                 //Roo.log("parent is missing level");
27319                   
27320                 parent.removeChild(n);
27321                  
27322                 return;
27323             }
27324             
27325             var margin = style['margin-left'];
27326             if (typeof(margin_to_depth[margin]) == 'undefined') {
27327                 max_margins++;
27328                 margin_to_depth[margin] = max_margins;
27329             }
27330             nlvl = margin_to_depth[margin] ;
27331              
27332             if (nlvl > lvl) {
27333                 //new indent
27334                 var nul = doc.createElement(listtype); // what about number lists...
27335                 if (!last_li) {
27336                     last_li = doc.createElement('li');
27337                     stack[lvl].appendChild(last_li);
27338                 }
27339                 last_li.appendChild(nul);
27340                 stack[nlvl] = nul;
27341                 
27342             }
27343             lvl = nlvl;
27344             
27345             // not starting at 1..
27346             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27347                 stack[nlvl].setAttribute("start", num);
27348             }
27349             
27350             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27351             last_li = nli;
27352             nli.innerHTML = n.innerHTML;
27353             //Roo.log("innerHTML = " + n.innerHTML);
27354             parent.removeChild(n);
27355             
27356              
27357              
27358             
27359         },this);
27360         
27361         
27362         
27363         
27364     },
27365     
27366     replaceImageTable : function(doc)
27367     {
27368          /*
27369           <table cellpadding=0 cellspacing=0 align=left>
27370   <tr>
27371    <td width=423 height=0></td>
27372   </tr>
27373   <tr>
27374    <td></td>
27375    <td><img width=601 height=401
27376    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27377    v:shapes="Picture_x0020_2"></td>
27378   </tr>
27379  </table>
27380  */
27381         var imgs = Array.from(doc.getElementsByTagName('img'));
27382         Roo.each(imgs, function(img) {
27383             var td = img.parentNode;
27384             if (td.nodeName !=  'TD') {
27385                 return;
27386             }
27387             var tr = td.parentNode;
27388             if (tr.nodeName !=  'TR') {
27389                 return;
27390             }
27391             var tbody = tr.parentNode;
27392             if (tbody.nodeName !=  'TBODY') {
27393                 return;
27394             }
27395             var table = tbody.parentNode;
27396             if (table.nodeName !=  'TABLE') {
27397                 return;
27398             }
27399             // first row..
27400             
27401             if (table.getElementsByTagName('tr').length != 2) {
27402                 return;
27403             }
27404             if (table.getElementsByTagName('td').length != 3) {
27405                 return;
27406             }
27407             if (table.innerText.trim() != '') {
27408                 return;
27409             }
27410             var p = table.parentNode;
27411             img.parentNode.removeChild(img);
27412             p.insertBefore(img, table);
27413             p.removeChild(table);
27414             
27415             
27416             
27417         });
27418         
27419       
27420     }
27421     
27422 });
27423 /**
27424  * @class Roo.htmleditor.FilterStyleToTag
27425  * part of the word stuff... - certain 'styles' should be converted to tags.
27426  * eg.
27427  *   font-weight: bold -> bold
27428  *   ?? super / subscrit etc..
27429  * 
27430  * @constructor
27431 * Run a new style to tag filter.
27432 * @param {Object} config Configuration options
27433  */
27434 Roo.htmleditor.FilterStyleToTag = function(cfg)
27435 {
27436     
27437     this.tags = {
27438         B  : [ 'fontWeight' , 'bold'],
27439         I :  [ 'fontStyle' , 'italic'],
27440         //pre :  [ 'font-style' , 'italic'],
27441         // h1.. h6 ?? font-size?
27442         SUP : [ 'verticalAlign' , 'super' ],
27443         SUB : [ 'verticalAlign' , 'sub' ]
27444         
27445         
27446     };
27447     
27448     Roo.apply(this, cfg);
27449      
27450     
27451     this.walk(cfg.node);
27452     
27453     
27454     
27455 }
27456
27457
27458 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27459 {
27460     tag: true, // all tags
27461     
27462     tags : false,
27463     
27464     
27465     replaceTag : function(node)
27466     {
27467         
27468         
27469         if (node.getAttribute("style") === null) {
27470             return true;
27471         }
27472         var inject = [];
27473         for (var k in this.tags) {
27474             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27475                 inject.push(k);
27476                 node.style.removeProperty(this.tags[k][0]);
27477             }
27478         }
27479         if (!inject.length) {
27480             return true; 
27481         }
27482         var cn = Array.from(node.childNodes);
27483         var nn = node;
27484         Roo.each(inject, function(t) {
27485             var nc = node.ownerDocument.createElement(t);
27486             nn.appendChild(nc);
27487             nn = nc;
27488         });
27489         for(var i = 0;i < cn.length;cn++) {
27490             node.removeChild(cn[i]);
27491             nn.appendChild(cn[i]);
27492         }
27493         return true /// iterate thru
27494     }
27495     
27496 })/**
27497  * @class Roo.htmleditor.FilterLongBr
27498  * BR/BR/BR - keep a maximum of 2...
27499  * @constructor
27500  * Run a new Long BR Filter
27501  * @param {Object} config Configuration options
27502  */
27503
27504 Roo.htmleditor.FilterLongBr = function(cfg)
27505 {
27506     // no need to apply config.
27507     this.walk(cfg.node);
27508 }
27509
27510 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27511 {
27512     
27513      
27514     tag : 'BR',
27515     
27516      
27517     replaceTag : function(node)
27518     {
27519         
27520         var ps = node.nextSibling;
27521         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27522             ps = ps.nextSibling;
27523         }
27524         
27525         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27526             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27527             return false;
27528         }
27529         
27530         if (!ps || ps.nodeType != 1) {
27531             return false;
27532         }
27533         
27534         if (!ps || ps.tagName != 'BR') {
27535            
27536             return false;
27537         }
27538         
27539         
27540         
27541         
27542         
27543         if (!node.previousSibling) {
27544             return false;
27545         }
27546         var ps = node.previousSibling;
27547         
27548         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27549             ps = ps.previousSibling;
27550         }
27551         if (!ps || ps.nodeType != 1) {
27552             return false;
27553         }
27554         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27555         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27556             return false;
27557         }
27558         
27559         node.parentNode.removeChild(node); // remove me...
27560         
27561         return false; // no need to do children
27562
27563     }
27564     
27565 }); 
27566
27567 /**
27568  * @class Roo.htmleditor.FilterBlock
27569  * removes id / data-block and contenteditable that are associated with blocks
27570  * usage should be done on a cloned copy of the dom
27571  * @constructor
27572 * Run a new Attribute Filter { node : xxxx }}
27573 * @param {Object} config Configuration options
27574  */
27575 Roo.htmleditor.FilterBlock = function(cfg)
27576 {
27577     Roo.apply(this, cfg);
27578     var qa = cfg.node.querySelectorAll;
27579     this.removeAttributes('data-block');
27580     this.removeAttributes('contenteditable');
27581     this.removeAttributes('id');
27582     
27583 }
27584
27585 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27586 {
27587     node: true, // all tags
27588      
27589      
27590     removeAttributes : function(attr)
27591     {
27592         var ar = this.node.querySelectorAll('*[' + attr + ']');
27593         for (var i =0;i<ar.length;i++) {
27594             ar[i].removeAttribute(attr);
27595         }
27596     }
27597         
27598         
27599         
27600     
27601 });
27602 /***
27603  * This is based loosely on tinymce 
27604  * @class Roo.htmleditor.TidySerializer
27605  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27606  * @constructor
27607  * @method Serializer
27608  * @param {Object} settings Name/value settings object.
27609  */
27610
27611
27612 Roo.htmleditor.TidySerializer = function(settings)
27613 {
27614     Roo.apply(this, settings);
27615     
27616     this.writer = new Roo.htmleditor.TidyWriter(settings);
27617     
27618     
27619
27620 };
27621 Roo.htmleditor.TidySerializer.prototype = {
27622     
27623     /**
27624      * @param {boolean} inner do the inner of the node.
27625      */
27626     inner : false,
27627     
27628     writer : false,
27629     
27630     /**
27631     * Serializes the specified node into a string.
27632     *
27633     * @example
27634     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27635     * @method serialize
27636     * @param {DomElement} node Node instance to serialize.
27637     * @return {String} String with HTML based on DOM tree.
27638     */
27639     serialize : function(node) {
27640         
27641         // = settings.validate;
27642         var writer = this.writer;
27643         var self  = this;
27644         this.handlers = {
27645             // #text
27646             3: function(node) {
27647                 
27648                 writer.text(node.nodeValue, node);
27649             },
27650             // #comment
27651             8: function(node) {
27652                 writer.comment(node.nodeValue);
27653             },
27654             // Processing instruction
27655             7: function(node) {
27656                 writer.pi(node.name, node.nodeValue);
27657             },
27658             // Doctype
27659             10: function(node) {
27660                 writer.doctype(node.nodeValue);
27661             },
27662             // CDATA
27663             4: function(node) {
27664                 writer.cdata(node.nodeValue);
27665             },
27666             // Document fragment
27667             11: function(node) {
27668                 node = node.firstChild;
27669                 if (!node) {
27670                     return;
27671                 }
27672                 while(node) {
27673                     self.walk(node);
27674                     node = node.nextSibling
27675                 }
27676             }
27677         };
27678         writer.reset();
27679         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27680         return writer.getContent();
27681     },
27682
27683     walk: function(node)
27684     {
27685         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27686             handler = this.handlers[node.nodeType];
27687             
27688         if (handler) {
27689             handler(node);
27690             return;
27691         }
27692     
27693         var name = node.nodeName;
27694         var isEmpty = node.childNodes.length < 1;
27695       
27696         var writer = this.writer;
27697         var attrs = node.attributes;
27698         // Sort attributes
27699         
27700         writer.start(node.nodeName, attrs, isEmpty, node);
27701         if (isEmpty) {
27702             return;
27703         }
27704         node = node.firstChild;
27705         if (!node) {
27706             writer.end(name);
27707             return;
27708         }
27709         while (node) {
27710             this.walk(node);
27711             node = node.nextSibling;
27712         }
27713         writer.end(name);
27714         
27715     
27716     }
27717     // Serialize element and treat all non elements as fragments
27718    
27719 }; 
27720
27721 /***
27722  * This is based loosely on tinymce 
27723  * @class Roo.htmleditor.TidyWriter
27724  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27725  *
27726  * Known issues?
27727  * - not tested much with 'PRE' formated elements.
27728  * 
27729  *
27730  *
27731  */
27732
27733 Roo.htmleditor.TidyWriter = function(settings)
27734 {
27735     
27736     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27737     Roo.apply(this, settings);
27738     this.html = [];
27739     this.state = [];
27740      
27741     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27742   
27743 }
27744 Roo.htmleditor.TidyWriter.prototype = {
27745
27746  
27747     state : false,
27748     
27749     indent :  '  ',
27750     
27751     // part of state...
27752     indentstr : '',
27753     in_pre: false,
27754     in_inline : false,
27755     last_inline : false,
27756     encode : false,
27757      
27758     
27759             /**
27760     * Writes the a start element such as <p id="a">.
27761     *
27762     * @method start
27763     * @param {String} name Name of the element.
27764     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27765     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27766     */
27767     start: function(name, attrs, empty, node)
27768     {
27769         var i, l, attr, value;
27770         
27771         // there are some situations where adding line break && indentation will not work. will not work.
27772         // <span / b / i ... formating?
27773         
27774         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27775         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27776         
27777         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27778         
27779         var add_lb = name == 'BR' ? false : in_inline;
27780         
27781         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27782             i_inline = false;
27783         }
27784
27785         var indentstr =  this.indentstr;
27786         
27787         // e_inline = elements that can be inline, but still allow \n before and after?
27788         // only 'BR' ??? any others?
27789         
27790         // ADD LINE BEFORE tage
27791         if (!this.in_pre) {
27792             if (in_inline) {
27793                 //code
27794                 if (name == 'BR') {
27795                     this.addLine();
27796                 } else if (this.lastElementEndsWS()) {
27797                     this.addLine();
27798                 } else{
27799                     // otherwise - no new line. (and dont indent.)
27800                     indentstr = '';
27801                 }
27802                 
27803             } else {
27804                 this.addLine();
27805             }
27806         } else {
27807             indentstr = '';
27808         }
27809         
27810         this.html.push(indentstr + '<', name.toLowerCase());
27811         
27812         if (attrs) {
27813             for (i = 0, l = attrs.length; i < l; i++) {
27814                 attr = attrs[i];
27815                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27816             }
27817         }
27818      
27819         if (empty) {
27820             if (is_short) {
27821                 this.html[this.html.length] = '/>';
27822             } else {
27823                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27824             }
27825             var e_inline = name == 'BR' ? false : this.in_inline;
27826             
27827             if (!e_inline && !this.in_pre) {
27828                 this.addLine();
27829             }
27830             return;
27831         
27832         }
27833         // not empty..
27834         this.html[this.html.length] = '>';
27835         
27836         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27837         /*
27838         if (!in_inline && !in_pre) {
27839             var cn = node.firstChild;
27840             while(cn) {
27841                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27842                     in_inline = true
27843                     break;
27844                 }
27845                 cn = cn.nextSibling;
27846             }
27847              
27848         }
27849         */
27850         
27851         
27852         this.pushState({
27853             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27854             in_pre : in_pre,
27855             in_inline :  in_inline
27856         });
27857         // add a line after if we are not in a
27858         
27859         if (!in_inline && !in_pre) {
27860             this.addLine();
27861         }
27862         
27863             
27864          
27865         
27866     },
27867     
27868     lastElementEndsWS : function()
27869     {
27870         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27871         if (value === false) {
27872             return true;
27873         }
27874         return value.match(/\s+$/);
27875         
27876     },
27877     
27878     /**
27879      * Writes the a end element such as </p>.
27880      *
27881      * @method end
27882      * @param {String} name Name of the element.
27883      */
27884     end: function(name) {
27885         var value;
27886         this.popState();
27887         var indentstr = '';
27888         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27889         
27890         if (!this.in_pre && !in_inline) {
27891             this.addLine();
27892             indentstr  = this.indentstr;
27893         }
27894         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27895         this.last_inline = in_inline;
27896         
27897         // pop the indent state..
27898     },
27899     /**
27900      * Writes a text node.
27901      *
27902      * In pre - we should not mess with the contents.
27903      * 
27904      *
27905      * @method text
27906      * @param {String} text String to write out.
27907      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27908      */
27909     text: function(in_text, node)
27910     {
27911         // if not in whitespace critical
27912         if (in_text.length < 1) {
27913             return;
27914         }
27915         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27916         
27917         if (this.in_pre) {
27918             this.html[this.html.length] =  text;
27919             return;   
27920         }
27921         
27922         if (this.in_inline) {
27923             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27924             if (text != ' ') {
27925                 text = text.replace(/\s+/,' ');  // all white space to single white space
27926                 
27927                     
27928                 // if next tag is '<BR>', then we can trim right..
27929                 if (node.nextSibling &&
27930                     node.nextSibling.nodeType == 1 &&
27931                     node.nextSibling.nodeName == 'BR' )
27932                 {
27933                     text = text.replace(/\s+$/g,'');
27934                 }
27935                 // if previous tag was a BR, we can also trim..
27936                 if (node.previousSibling &&
27937                     node.previousSibling.nodeType == 1 &&
27938                     node.previousSibling.nodeName == 'BR' )
27939                 {
27940                     text = this.indentstr +  text.replace(/^\s+/g,'');
27941                 }
27942                 if (text.match(/\n/)) {
27943                     text = text.replace(
27944                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27945                     );
27946                     // remoeve the last whitespace / line break.
27947                     text = text.replace(/\n\s+$/,'');
27948                 }
27949                 // repace long lines
27950                 
27951             }
27952              
27953             this.html[this.html.length] =  text;
27954             return;   
27955         }
27956         // see if previous element was a inline element.
27957         var indentstr = this.indentstr;
27958    
27959         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27960         
27961         // should trim left?
27962         if (node.previousSibling &&
27963             node.previousSibling.nodeType == 1 &&
27964             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27965         {
27966             indentstr = '';
27967             
27968         } else {
27969             this.addLine();
27970             text = text.replace(/^\s+/,''); // trim left
27971           
27972         }
27973         // should trim right?
27974         if (node.nextSibling &&
27975             node.nextSibling.nodeType == 1 &&
27976             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27977         {
27978           // noop
27979             
27980         }  else {
27981             text = text.replace(/\s+$/,''); // trim right
27982         }
27983          
27984               
27985         
27986         
27987         
27988         if (text.length < 1) {
27989             return;
27990         }
27991         if (!text.match(/\n/)) {
27992             this.html.push(indentstr + text);
27993             return;
27994         }
27995         
27996         text = this.indentstr + text.replace(
27997             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27998         );
27999         // remoeve the last whitespace / line break.
28000         text = text.replace(/\s+$/,''); 
28001         
28002         this.html.push(text);
28003         
28004         // split and indent..
28005         
28006         
28007     },
28008     /**
28009      * Writes a cdata node such as <![CDATA[data]]>.
28010      *
28011      * @method cdata
28012      * @param {String} text String to write out inside the cdata.
28013      */
28014     cdata: function(text) {
28015         this.html.push('<![CDATA[', text, ']]>');
28016     },
28017     /**
28018     * Writes a comment node such as <!-- Comment -->.
28019     *
28020     * @method cdata
28021     * @param {String} text String to write out inside the comment.
28022     */
28023    comment: function(text) {
28024        this.html.push('<!--', text, '-->');
28025    },
28026     /**
28027      * Writes a PI node such as <?xml attr="value" ?>.
28028      *
28029      * @method pi
28030      * @param {String} name Name of the pi.
28031      * @param {String} text String to write out inside the pi.
28032      */
28033     pi: function(name, text) {
28034         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28035         this.indent != '' && this.html.push('\n');
28036     },
28037     /**
28038      * Writes a doctype node such as <!DOCTYPE data>.
28039      *
28040      * @method doctype
28041      * @param {String} text String to write out inside the doctype.
28042      */
28043     doctype: function(text) {
28044         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28045     },
28046     /**
28047      * Resets the internal buffer if one wants to reuse the writer.
28048      *
28049      * @method reset
28050      */
28051     reset: function() {
28052         this.html.length = 0;
28053         this.state = [];
28054         this.pushState({
28055             indentstr : '',
28056             in_pre : false, 
28057             in_inline : false
28058         })
28059     },
28060     /**
28061      * Returns the contents that got serialized.
28062      *
28063      * @method getContent
28064      * @return {String} HTML contents that got written down.
28065      */
28066     getContent: function() {
28067         return this.html.join('').replace(/\n$/, '');
28068     },
28069     
28070     pushState : function(cfg)
28071     {
28072         this.state.push(cfg);
28073         Roo.apply(this, cfg);
28074     },
28075     
28076     popState : function()
28077     {
28078         if (this.state.length < 1) {
28079             return; // nothing to push
28080         }
28081         var cfg = {
28082             in_pre: false,
28083             indentstr : ''
28084         };
28085         this.state.pop();
28086         if (this.state.length > 0) {
28087             cfg = this.state[this.state.length-1]; 
28088         }
28089         Roo.apply(this, cfg);
28090     },
28091     
28092     addLine: function()
28093     {
28094         if (this.html.length < 1) {
28095             return;
28096         }
28097         
28098         
28099         var value = this.html[this.html.length - 1];
28100         if (value.length > 0 && '\n' !== value) {
28101             this.html.push('\n');
28102         }
28103     }
28104     
28105     
28106 //'pre script noscript style textarea video audio iframe object code'
28107 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28108 // inline 
28109 };
28110
28111 Roo.htmleditor.TidyWriter.inline_elements = [
28112         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28113         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28114 ];
28115 Roo.htmleditor.TidyWriter.shortend_elements = [
28116     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28117     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28118 ];
28119
28120 Roo.htmleditor.TidyWriter.whitespace_elements = [
28121     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28122 ];/***
28123  * This is based loosely on tinymce 
28124  * @class Roo.htmleditor.TidyEntities
28125  * @static
28126  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28127  *
28128  * Not 100% sure this is actually used or needed.
28129  */
28130
28131 Roo.htmleditor.TidyEntities = {
28132     
28133     /**
28134      * initialize data..
28135      */
28136     init : function (){
28137      
28138         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28139        
28140     },
28141
28142
28143     buildEntitiesLookup: function(items, radix) {
28144         var i, chr, entity, lookup = {};
28145         if (!items) {
28146             return {};
28147         }
28148         items = typeof(items) == 'string' ? items.split(',') : items;
28149         radix = radix || 10;
28150         // Build entities lookup table
28151         for (i = 0; i < items.length; i += 2) {
28152             chr = String.fromCharCode(parseInt(items[i], radix));
28153             // Only add non base entities
28154             if (!this.baseEntities[chr]) {
28155                 entity = '&' + items[i + 1] + ';';
28156                 lookup[chr] = entity;
28157                 lookup[entity] = chr;
28158             }
28159         }
28160         return lookup;
28161         
28162     },
28163     
28164     asciiMap : {
28165             128: '€',
28166             130: '‚',
28167             131: 'ƒ',
28168             132: '„',
28169             133: '…',
28170             134: '†',
28171             135: '‡',
28172             136: 'ˆ',
28173             137: '‰',
28174             138: 'Š',
28175             139: '‹',
28176             140: 'Œ',
28177             142: 'Ž',
28178             145: '‘',
28179             146: '’',
28180             147: '“',
28181             148: '”',
28182             149: '•',
28183             150: '–',
28184             151: '—',
28185             152: '˜',
28186             153: '™',
28187             154: 'š',
28188             155: '›',
28189             156: 'œ',
28190             158: 'ž',
28191             159: 'Ÿ'
28192     },
28193     // Raw entities
28194     baseEntities : {
28195         '"': '&quot;',
28196         // Needs to be escaped since the YUI compressor would otherwise break the code
28197         '\'': '&#39;',
28198         '<': '&lt;',
28199         '>': '&gt;',
28200         '&': '&amp;',
28201         '`': '&#96;'
28202     },
28203     // Reverse lookup table for raw entities
28204     reverseEntities : {
28205         '&lt;': '<',
28206         '&gt;': '>',
28207         '&amp;': '&',
28208         '&quot;': '"',
28209         '&apos;': '\''
28210     },
28211     
28212     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28213     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28214     rawCharsRegExp : /[<>&\"\']/g,
28215     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28216     namedEntities  : false,
28217     namedEntitiesData : [ 
28218         '50',
28219         'nbsp',
28220         '51',
28221         'iexcl',
28222         '52',
28223         'cent',
28224         '53',
28225         'pound',
28226         '54',
28227         'curren',
28228         '55',
28229         'yen',
28230         '56',
28231         'brvbar',
28232         '57',
28233         'sect',
28234         '58',
28235         'uml',
28236         '59',
28237         'copy',
28238         '5a',
28239         'ordf',
28240         '5b',
28241         'laquo',
28242         '5c',
28243         'not',
28244         '5d',
28245         'shy',
28246         '5e',
28247         'reg',
28248         '5f',
28249         'macr',
28250         '5g',
28251         'deg',
28252         '5h',
28253         'plusmn',
28254         '5i',
28255         'sup2',
28256         '5j',
28257         'sup3',
28258         '5k',
28259         'acute',
28260         '5l',
28261         'micro',
28262         '5m',
28263         'para',
28264         '5n',
28265         'middot',
28266         '5o',
28267         'cedil',
28268         '5p',
28269         'sup1',
28270         '5q',
28271         'ordm',
28272         '5r',
28273         'raquo',
28274         '5s',
28275         'frac14',
28276         '5t',
28277         'frac12',
28278         '5u',
28279         'frac34',
28280         '5v',
28281         'iquest',
28282         '60',
28283         'Agrave',
28284         '61',
28285         'Aacute',
28286         '62',
28287         'Acirc',
28288         '63',
28289         'Atilde',
28290         '64',
28291         'Auml',
28292         '65',
28293         'Aring',
28294         '66',
28295         'AElig',
28296         '67',
28297         'Ccedil',
28298         '68',
28299         'Egrave',
28300         '69',
28301         'Eacute',
28302         '6a',
28303         'Ecirc',
28304         '6b',
28305         'Euml',
28306         '6c',
28307         'Igrave',
28308         '6d',
28309         'Iacute',
28310         '6e',
28311         'Icirc',
28312         '6f',
28313         'Iuml',
28314         '6g',
28315         'ETH',
28316         '6h',
28317         'Ntilde',
28318         '6i',
28319         'Ograve',
28320         '6j',
28321         'Oacute',
28322         '6k',
28323         'Ocirc',
28324         '6l',
28325         'Otilde',
28326         '6m',
28327         'Ouml',
28328         '6n',
28329         'times',
28330         '6o',
28331         'Oslash',
28332         '6p',
28333         'Ugrave',
28334         '6q',
28335         'Uacute',
28336         '6r',
28337         'Ucirc',
28338         '6s',
28339         'Uuml',
28340         '6t',
28341         'Yacute',
28342         '6u',
28343         'THORN',
28344         '6v',
28345         'szlig',
28346         '70',
28347         'agrave',
28348         '71',
28349         'aacute',
28350         '72',
28351         'acirc',
28352         '73',
28353         'atilde',
28354         '74',
28355         'auml',
28356         '75',
28357         'aring',
28358         '76',
28359         'aelig',
28360         '77',
28361         'ccedil',
28362         '78',
28363         'egrave',
28364         '79',
28365         'eacute',
28366         '7a',
28367         'ecirc',
28368         '7b',
28369         'euml',
28370         '7c',
28371         'igrave',
28372         '7d',
28373         'iacute',
28374         '7e',
28375         'icirc',
28376         '7f',
28377         'iuml',
28378         '7g',
28379         'eth',
28380         '7h',
28381         'ntilde',
28382         '7i',
28383         'ograve',
28384         '7j',
28385         'oacute',
28386         '7k',
28387         'ocirc',
28388         '7l',
28389         'otilde',
28390         '7m',
28391         'ouml',
28392         '7n',
28393         'divide',
28394         '7o',
28395         'oslash',
28396         '7p',
28397         'ugrave',
28398         '7q',
28399         'uacute',
28400         '7r',
28401         'ucirc',
28402         '7s',
28403         'uuml',
28404         '7t',
28405         'yacute',
28406         '7u',
28407         'thorn',
28408         '7v',
28409         'yuml',
28410         'ci',
28411         'fnof',
28412         'sh',
28413         'Alpha',
28414         'si',
28415         'Beta',
28416         'sj',
28417         'Gamma',
28418         'sk',
28419         'Delta',
28420         'sl',
28421         'Epsilon',
28422         'sm',
28423         'Zeta',
28424         'sn',
28425         'Eta',
28426         'so',
28427         'Theta',
28428         'sp',
28429         'Iota',
28430         'sq',
28431         'Kappa',
28432         'sr',
28433         'Lambda',
28434         'ss',
28435         'Mu',
28436         'st',
28437         'Nu',
28438         'su',
28439         'Xi',
28440         'sv',
28441         'Omicron',
28442         't0',
28443         'Pi',
28444         't1',
28445         'Rho',
28446         't3',
28447         'Sigma',
28448         't4',
28449         'Tau',
28450         't5',
28451         'Upsilon',
28452         't6',
28453         'Phi',
28454         't7',
28455         'Chi',
28456         't8',
28457         'Psi',
28458         't9',
28459         'Omega',
28460         'th',
28461         'alpha',
28462         'ti',
28463         'beta',
28464         'tj',
28465         'gamma',
28466         'tk',
28467         'delta',
28468         'tl',
28469         'epsilon',
28470         'tm',
28471         'zeta',
28472         'tn',
28473         'eta',
28474         'to',
28475         'theta',
28476         'tp',
28477         'iota',
28478         'tq',
28479         'kappa',
28480         'tr',
28481         'lambda',
28482         'ts',
28483         'mu',
28484         'tt',
28485         'nu',
28486         'tu',
28487         'xi',
28488         'tv',
28489         'omicron',
28490         'u0',
28491         'pi',
28492         'u1',
28493         'rho',
28494         'u2',
28495         'sigmaf',
28496         'u3',
28497         'sigma',
28498         'u4',
28499         'tau',
28500         'u5',
28501         'upsilon',
28502         'u6',
28503         'phi',
28504         'u7',
28505         'chi',
28506         'u8',
28507         'psi',
28508         'u9',
28509         'omega',
28510         'uh',
28511         'thetasym',
28512         'ui',
28513         'upsih',
28514         'um',
28515         'piv',
28516         '812',
28517         'bull',
28518         '816',
28519         'hellip',
28520         '81i',
28521         'prime',
28522         '81j',
28523         'Prime',
28524         '81u',
28525         'oline',
28526         '824',
28527         'frasl',
28528         '88o',
28529         'weierp',
28530         '88h',
28531         'image',
28532         '88s',
28533         'real',
28534         '892',
28535         'trade',
28536         '89l',
28537         'alefsym',
28538         '8cg',
28539         'larr',
28540         '8ch',
28541         'uarr',
28542         '8ci',
28543         'rarr',
28544         '8cj',
28545         'darr',
28546         '8ck',
28547         'harr',
28548         '8dl',
28549         'crarr',
28550         '8eg',
28551         'lArr',
28552         '8eh',
28553         'uArr',
28554         '8ei',
28555         'rArr',
28556         '8ej',
28557         'dArr',
28558         '8ek',
28559         'hArr',
28560         '8g0',
28561         'forall',
28562         '8g2',
28563         'part',
28564         '8g3',
28565         'exist',
28566         '8g5',
28567         'empty',
28568         '8g7',
28569         'nabla',
28570         '8g8',
28571         'isin',
28572         '8g9',
28573         'notin',
28574         '8gb',
28575         'ni',
28576         '8gf',
28577         'prod',
28578         '8gh',
28579         'sum',
28580         '8gi',
28581         'minus',
28582         '8gn',
28583         'lowast',
28584         '8gq',
28585         'radic',
28586         '8gt',
28587         'prop',
28588         '8gu',
28589         'infin',
28590         '8h0',
28591         'ang',
28592         '8h7',
28593         'and',
28594         '8h8',
28595         'or',
28596         '8h9',
28597         'cap',
28598         '8ha',
28599         'cup',
28600         '8hb',
28601         'int',
28602         '8hk',
28603         'there4',
28604         '8hs',
28605         'sim',
28606         '8i5',
28607         'cong',
28608         '8i8',
28609         'asymp',
28610         '8j0',
28611         'ne',
28612         '8j1',
28613         'equiv',
28614         '8j4',
28615         'le',
28616         '8j5',
28617         'ge',
28618         '8k2',
28619         'sub',
28620         '8k3',
28621         'sup',
28622         '8k4',
28623         'nsub',
28624         '8k6',
28625         'sube',
28626         '8k7',
28627         'supe',
28628         '8kl',
28629         'oplus',
28630         '8kn',
28631         'otimes',
28632         '8l5',
28633         'perp',
28634         '8m5',
28635         'sdot',
28636         '8o8',
28637         'lceil',
28638         '8o9',
28639         'rceil',
28640         '8oa',
28641         'lfloor',
28642         '8ob',
28643         'rfloor',
28644         '8p9',
28645         'lang',
28646         '8pa',
28647         'rang',
28648         '9ea',
28649         'loz',
28650         '9j0',
28651         'spades',
28652         '9j3',
28653         'clubs',
28654         '9j5',
28655         'hearts',
28656         '9j6',
28657         'diams',
28658         'ai',
28659         'OElig',
28660         'aj',
28661         'oelig',
28662         'b0',
28663         'Scaron',
28664         'b1',
28665         'scaron',
28666         'bo',
28667         'Yuml',
28668         'm6',
28669         'circ',
28670         'ms',
28671         'tilde',
28672         '802',
28673         'ensp',
28674         '803',
28675         'emsp',
28676         '809',
28677         'thinsp',
28678         '80c',
28679         'zwnj',
28680         '80d',
28681         'zwj',
28682         '80e',
28683         'lrm',
28684         '80f',
28685         'rlm',
28686         '80j',
28687         'ndash',
28688         '80k',
28689         'mdash',
28690         '80o',
28691         'lsquo',
28692         '80p',
28693         'rsquo',
28694         '80q',
28695         'sbquo',
28696         '80s',
28697         'ldquo',
28698         '80t',
28699         'rdquo',
28700         '80u',
28701         'bdquo',
28702         '810',
28703         'dagger',
28704         '811',
28705         'Dagger',
28706         '81g',
28707         'permil',
28708         '81p',
28709         'lsaquo',
28710         '81q',
28711         'rsaquo',
28712         '85c',
28713         'euro'
28714     ],
28715
28716          
28717     /**
28718      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28719      *
28720      * @method encodeRaw
28721      * @param {String} text Text to encode.
28722      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28723      * @return {String} Entity encoded text.
28724      */
28725     encodeRaw: function(text, attr)
28726     {
28727         var t = this;
28728         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28729             return t.baseEntities[chr] || chr;
28730         });
28731     },
28732     /**
28733      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28734      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28735      * and is exposed as the DOMUtils.encode function.
28736      *
28737      * @method encodeAllRaw
28738      * @param {String} text Text to encode.
28739      * @return {String} Entity encoded text.
28740      */
28741     encodeAllRaw: function(text) {
28742         var t = this;
28743         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28744             return t.baseEntities[chr] || chr;
28745         });
28746     },
28747     /**
28748      * Encodes the specified string using numeric entities. The core entities will be
28749      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28750      *
28751      * @method encodeNumeric
28752      * @param {String} text Text to encode.
28753      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28754      * @return {String} Entity encoded text.
28755      */
28756     encodeNumeric: function(text, attr) {
28757         var t = this;
28758         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28759             // Multi byte sequence convert it to a single entity
28760             if (chr.length > 1) {
28761                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28762             }
28763             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28764         });
28765     },
28766     /**
28767      * Encodes the specified string using named entities. The core entities will be encoded
28768      * as named ones but all non lower ascii characters will be encoded into named entities.
28769      *
28770      * @method encodeNamed
28771      * @param {String} text Text to encode.
28772      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28773      * @param {Object} entities Optional parameter with entities to use.
28774      * @return {String} Entity encoded text.
28775      */
28776     encodeNamed: function(text, attr, entities) {
28777         var t = this;
28778         entities = entities || this.namedEntities;
28779         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28780             return t.baseEntities[chr] || entities[chr] || chr;
28781         });
28782     },
28783     /**
28784      * Returns an encode function based on the name(s) and it's optional entities.
28785      *
28786      * @method getEncodeFunc
28787      * @param {String} name Comma separated list of encoders for example named,numeric.
28788      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28789      * @return {function} Encode function to be used.
28790      */
28791     getEncodeFunc: function(name, entities) {
28792         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28793         var t = this;
28794         function encodeNamedAndNumeric(text, attr) {
28795             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28796                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28797             });
28798         }
28799
28800         function encodeCustomNamed(text, attr) {
28801             return t.encodeNamed(text, attr, entities);
28802         }
28803         // Replace + with , to be compatible with previous TinyMCE versions
28804         name = this.makeMap(name.replace(/\+/g, ','));
28805         // Named and numeric encoder
28806         if (name.named && name.numeric) {
28807             return this.encodeNamedAndNumeric;
28808         }
28809         // Named encoder
28810         if (name.named) {
28811             // Custom names
28812             if (entities) {
28813                 return encodeCustomNamed;
28814             }
28815             return this.encodeNamed;
28816         }
28817         // Numeric
28818         if (name.numeric) {
28819             return this.encodeNumeric;
28820         }
28821         // Raw encoder
28822         return this.encodeRaw;
28823     },
28824     /**
28825      * Decodes the specified string, this will replace entities with raw UTF characters.
28826      *
28827      * @method decode
28828      * @param {String} text Text to entity decode.
28829      * @return {String} Entity decoded string.
28830      */
28831     decode: function(text)
28832     {
28833         var  t = this;
28834         return text.replace(this.entityRegExp, function(all, numeric) {
28835             if (numeric) {
28836                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28837                 // Support upper UTF
28838                 if (numeric > 65535) {
28839                     numeric -= 65536;
28840                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28841                 }
28842                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28843             }
28844             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28845         });
28846     },
28847     nativeDecode : function (text) {
28848         return text;
28849     },
28850     makeMap : function (items, delim, map) {
28851                 var i;
28852                 items = items || [];
28853                 delim = delim || ',';
28854                 if (typeof items == "string") {
28855                         items = items.split(delim);
28856                 }
28857                 map = map || {};
28858                 i = items.length;
28859                 while (i--) {
28860                         map[items[i]] = {};
28861                 }
28862                 return map;
28863         }
28864 };
28865     
28866     
28867     
28868 Roo.htmleditor.TidyEntities.init();
28869 /**
28870  * @class Roo.htmleditor.KeyEnter
28871  * Handle Enter press..
28872  * @cfg {Roo.HtmlEditorCore} core the editor.
28873  * @constructor
28874  * Create a new Filter.
28875  * @param {Object} config Configuration options
28876  */
28877
28878
28879
28880
28881
28882 Roo.htmleditor.KeyEnter = function(cfg) {
28883     Roo.apply(this, cfg);
28884     // this does not actually call walk as it's really just a abstract class
28885  
28886     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28887 }
28888
28889 //Roo.htmleditor.KeyEnter.i = 0;
28890
28891
28892 Roo.htmleditor.KeyEnter.prototype = {
28893     
28894     core : false,
28895     
28896     keypress : function(e)
28897     {
28898         if (e.charCode != 13 && e.charCode != 10) {
28899             Roo.log([e.charCode,e]);
28900             return true;
28901         }
28902         e.preventDefault();
28903         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28904         var doc = this.core.doc;
28905           //add a new line
28906        
28907     
28908         var sel = this.core.getSelection();
28909         var range = sel.getRangeAt(0);
28910         var n = range.commonAncestorContainer;
28911         var pc = range.closest([ 'ol', 'ul']);
28912         var pli = range.closest('li');
28913         if (!pc || e.ctrlKey) {
28914             // on it list, or ctrl pressed.
28915             if (!e.ctrlKey) {
28916                 sel.insertNode('br', 'after'); 
28917             } else {
28918                 // only do this if we have ctrl key..
28919                 var br = doc.createElement('br');
28920                 br.className = 'clear';
28921                 br.setAttribute('style', 'clear: both');
28922                 sel.insertNode(br, 'after'); 
28923             }
28924             
28925          
28926             this.core.undoManager.addEvent();
28927             this.core.fireEditorEvent(e);
28928             return false;
28929         }
28930         
28931         // deal with <li> insetion
28932         if (pli.innerText.trim() == '' &&
28933             pli.previousSibling &&
28934             pli.previousSibling.nodeName == 'LI' &&
28935             pli.previousSibling.innerText.trim() ==  '') {
28936             pli.parentNode.removeChild(pli.previousSibling);
28937             sel.cursorAfter(pc);
28938             this.core.undoManager.addEvent();
28939             this.core.fireEditorEvent(e);
28940             return false;
28941         }
28942     
28943         var li = doc.createElement('LI');
28944         li.innerHTML = '&nbsp;';
28945         if (!pli || !pli.firstSibling) {
28946             pc.appendChild(li);
28947         } else {
28948             pli.parentNode.insertBefore(li, pli.firstSibling);
28949         }
28950         sel.cursorText (li.firstChild);
28951       
28952         this.core.undoManager.addEvent();
28953         this.core.fireEditorEvent(e);
28954
28955         return false;
28956         
28957     
28958         
28959         
28960          
28961     }
28962 };
28963      
28964 /**
28965  * @class Roo.htmleditor.Block
28966  * Base class for html editor blocks - do not use it directly .. extend it..
28967  * @cfg {DomElement} node The node to apply stuff to.
28968  * @cfg {String} friendly_name the name that appears in the context bar about this block
28969  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28970  
28971  * @constructor
28972  * Create a new Filter.
28973  * @param {Object} config Configuration options
28974  */
28975
28976 Roo.htmleditor.Block  = function(cfg)
28977 {
28978     // do nothing .. should not be called really.
28979 }
28980 /**
28981  * factory method to get the block from an element (using cache if necessary)
28982  * @static
28983  * @param {HtmlElement} the dom element
28984  */
28985 Roo.htmleditor.Block.factory = function(node)
28986 {
28987     var cc = Roo.htmleditor.Block.cache;
28988     var id = Roo.get(node).id;
28989     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28990         Roo.htmleditor.Block.cache[id].readElement(node);
28991         return Roo.htmleditor.Block.cache[id];
28992     }
28993     var db  = node.getAttribute('data-block');
28994     if (!db) {
28995         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28996     }
28997     var cls = Roo.htmleditor['Block' + db];
28998     if (typeof(cls) == 'undefined') {
28999         //Roo.log(node.getAttribute('data-block'));
29000         Roo.log("OOps missing block : " + 'Block' + db);
29001         return false;
29002     }
29003     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29004     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
29005 };
29006
29007 /**
29008  * initalize all Elements from content that are 'blockable'
29009  * @static
29010  * @param the body element
29011  */
29012 Roo.htmleditor.Block.initAll = function(body, type)
29013 {
29014     if (typeof(type) == 'undefined') {
29015         var ia = Roo.htmleditor.Block.initAll;
29016         ia(body,'table');
29017         ia(body,'td');
29018         ia(body,'figure');
29019         return;
29020     }
29021     Roo.each(Roo.get(body).query(type), function(e) {
29022         Roo.htmleditor.Block.factory(e);    
29023     },this);
29024 };
29025 // question goes here... do we need to clear out this cache sometimes?
29026 // or show we make it relivant to the htmleditor.
29027 Roo.htmleditor.Block.cache = {};
29028
29029 Roo.htmleditor.Block.prototype = {
29030     
29031     node : false,
29032     
29033      // used by context menu
29034     friendly_name : 'Based Block',
29035     
29036     // text for button to delete this element
29037     deleteTitle : false,
29038     
29039     context : false,
29040     /**
29041      * Update a node with values from this object
29042      * @param {DomElement} node
29043      */
29044     updateElement : function(node)
29045     {
29046         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29047     },
29048      /**
29049      * convert to plain HTML for calling insertAtCursor..
29050      */
29051     toHTML : function()
29052     {
29053         return Roo.DomHelper.markup(this.toObject());
29054     },
29055     /**
29056      * used by readEleemnt to extract data from a node
29057      * may need improving as it's pretty basic
29058      
29059      * @param {DomElement} node
29060      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29061      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29062      * @param {String} style the style property - eg. text-align
29063      */
29064     getVal : function(node, tag, attr, style)
29065     {
29066         var n = node;
29067         if (tag !== true && n.tagName != tag.toUpperCase()) {
29068             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29069             // but kiss for now.
29070             n = node.getElementsByTagName(tag).item(0);
29071         }
29072         if (!n) {
29073             return '';
29074         }
29075         if (attr === false) {
29076             return n;
29077         }
29078         if (attr == 'html') {
29079             return n.innerHTML;
29080         }
29081         if (attr == 'style') {
29082             return n.style[style]; 
29083         }
29084         
29085         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29086             
29087     },
29088     /**
29089      * create a DomHelper friendly object - for use with 
29090      * Roo.DomHelper.markup / overwrite / etc..
29091      * (override this)
29092      */
29093     toObject : function()
29094     {
29095         return {};
29096     },
29097       /**
29098      * Read a node that has a 'data-block' property - and extract the values from it.
29099      * @param {DomElement} node - the node
29100      */
29101     readElement : function(node)
29102     {
29103         
29104     } 
29105     
29106     
29107 };
29108
29109  
29110
29111 /**
29112  * @class Roo.htmleditor.BlockFigure
29113  * Block that has an image and a figcaption
29114  * @cfg {String} image_src the url for the image
29115  * @cfg {String} align (left|right) alignment for the block default left
29116  * @cfg {String} caption the text to appear below  (and in the alt tag)
29117  * @cfg {String} caption_display (block|none) display or not the caption
29118  * @cfg {String|number} image_width the width of the image number or %?
29119  * @cfg {String|number} image_height the height of the image number or %?
29120  * 
29121  * @constructor
29122  * Create a new Filter.
29123  * @param {Object} config Configuration options
29124  */
29125
29126 Roo.htmleditor.BlockFigure = function(cfg)
29127 {
29128     if (cfg.node) {
29129         this.readElement(cfg.node);
29130         this.updateElement(cfg.node);
29131     }
29132     Roo.apply(this, cfg);
29133 }
29134 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29135  
29136     
29137     // setable values.
29138     image_src: '',
29139     align: 'center',
29140     caption : '',
29141     caption_display : 'block',
29142     width : '100%',
29143     cls : '',
29144     href: '',
29145     video_url : '',
29146     
29147     // margin: '2%', not used
29148     
29149     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29150
29151     
29152     // used by context menu
29153     friendly_name : 'Image with caption',
29154     deleteTitle : "Delete Image and Caption",
29155     
29156     contextMenu : function(toolbar)
29157     {
29158         
29159         var block = function() {
29160             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29161         };
29162         
29163         
29164         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29165         
29166         var syncValue = toolbar.editorcore.syncValue;
29167         
29168         var fields = {};
29169         
29170         return [
29171              {
29172                 xtype : 'TextItem',
29173                 text : "Source: ",
29174                 xns : rooui.Toolbar  //Boostrap?
29175             },
29176             {
29177                 xtype : 'Button',
29178                 text: 'Change Image URL',
29179                  
29180                 listeners : {
29181                     click: function (btn, state)
29182                     {
29183                         var b = block();
29184                         
29185                         Roo.MessageBox.show({
29186                             title : "Image Source URL",
29187                             msg : "Enter the url for the image",
29188                             buttons: Roo.MessageBox.OKCANCEL,
29189                             fn: function(btn, val){
29190                                 if (btn != 'ok') {
29191                                     return;
29192                                 }
29193                                 b.image_src = val;
29194                                 b.updateElement();
29195                                 syncValue();
29196                                 toolbar.editorcore.onEditorEvent();
29197                             },
29198                             minWidth:250,
29199                             prompt:true,
29200                             //multiline: multiline,
29201                             modal : true,
29202                             value : b.image_src
29203                         });
29204                     }
29205                 },
29206                 xns : rooui.Toolbar
29207             },
29208          
29209             {
29210                 xtype : 'Button',
29211                 text: 'Change Link URL',
29212                  
29213                 listeners : {
29214                     click: function (btn, state)
29215                     {
29216                         var b = block();
29217                         
29218                         Roo.MessageBox.show({
29219                             title : "Link URL",
29220                             msg : "Enter the url for the link - leave blank to have no link",
29221                             buttons: Roo.MessageBox.OKCANCEL,
29222                             fn: function(btn, val){
29223                                 if (btn != 'ok') {
29224                                     return;
29225                                 }
29226                                 b.href = val;
29227                                 b.updateElement();
29228                                 syncValue();
29229                                 toolbar.editorcore.onEditorEvent();
29230                             },
29231                             minWidth:250,
29232                             prompt:true,
29233                             //multiline: multiline,
29234                             modal : true,
29235                             value : b.href
29236                         });
29237                     }
29238                 },
29239                 xns : rooui.Toolbar
29240             },
29241             {
29242                 xtype : 'Button',
29243                 text: 'Show Video URL',
29244                  
29245                 listeners : {
29246                     click: function (btn, state)
29247                     {
29248                         Roo.MessageBox.alert("Video URL",
29249                             block().video_url == '' ? 'This image is not linked ot a video' :
29250                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29251                     }
29252                 },
29253                 xns : rooui.Toolbar
29254             },
29255             
29256             
29257             {
29258                 xtype : 'TextItem',
29259                 text : "Width: ",
29260                 xns : rooui.Toolbar  //Boostrap?
29261             },
29262             {
29263                 xtype : 'ComboBox',
29264                 allowBlank : false,
29265                 displayField : 'val',
29266                 editable : true,
29267                 listWidth : 100,
29268                 triggerAction : 'all',
29269                 typeAhead : true,
29270                 valueField : 'val',
29271                 width : 70,
29272                 name : 'width',
29273                 listeners : {
29274                     select : function (combo, r, index)
29275                     {
29276                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29277                         var b = block();
29278                         b.width = r.get('val');
29279                         b.updateElement();
29280                         syncValue();
29281                         toolbar.editorcore.onEditorEvent();
29282                     }
29283                 },
29284                 xns : rooui.form,
29285                 store : {
29286                     xtype : 'SimpleStore',
29287                     data : [
29288                         ['100%'],
29289                         ['80%'],
29290                         ['50%'],
29291                         ['20%'],
29292                         ['10%']
29293                     ],
29294                     fields : [ 'val'],
29295                     xns : Roo.data
29296                 }
29297             },
29298             {
29299                 xtype : 'TextItem',
29300                 text : "Align: ",
29301                 xns : rooui.Toolbar  //Boostrap?
29302             },
29303             {
29304                 xtype : 'ComboBox',
29305                 allowBlank : false,
29306                 displayField : 'val',
29307                 editable : true,
29308                 listWidth : 100,
29309                 triggerAction : 'all',
29310                 typeAhead : true,
29311                 valueField : 'val',
29312                 width : 70,
29313                 name : 'align',
29314                 listeners : {
29315                     select : function (combo, r, index)
29316                     {
29317                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29318                         var b = block();
29319                         b.align = r.get('val');
29320                         b.updateElement();
29321                         syncValue();
29322                         toolbar.editorcore.onEditorEvent();
29323                     }
29324                 },
29325                 xns : rooui.form,
29326                 store : {
29327                     xtype : 'SimpleStore',
29328                     data : [
29329                         ['left'],
29330                         ['right'],
29331                         ['center']
29332                     ],
29333                     fields : [ 'val'],
29334                     xns : Roo.data
29335                 }
29336             },
29337             
29338               
29339             {
29340                 xtype : 'Button',
29341                 text: 'Hide Caption',
29342                 name : 'caption_display',
29343                 pressed : false,
29344                 enableToggle : true,
29345                 setValue : function(v) {
29346                     // this trigger toggle.
29347                      
29348                     this.setText(v ? "Hide Caption" : "Show Caption");
29349                     this.setPressed(v != 'block');
29350                 },
29351                 listeners : {
29352                     toggle: function (btn, state)
29353                     {
29354                         var b  = block();
29355                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29356                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29357                         b.updateElement();
29358                         syncValue();
29359                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29360                         toolbar.editorcore.onEditorEvent();
29361                     }
29362                 },
29363                 xns : rooui.Toolbar
29364             }
29365         ];
29366         
29367     },
29368     /**
29369      * create a DomHelper friendly object - for use with
29370      * Roo.DomHelper.markup / overwrite / etc..
29371      */
29372     toObject : function()
29373     {
29374         var d = document.createElement('div');
29375         d.innerHTML = this.caption;
29376         
29377         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29378         
29379         var iw = this.align == 'center' ? this.width : '100%';
29380         var img =   {
29381             tag : 'img',
29382             contenteditable : 'false',
29383             src : this.image_src,
29384             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29385             style: {
29386                 width : iw,
29387                 maxWidth : iw + ' !important', // this is not getting rendered?
29388                 margin : m  
29389                 
29390             }
29391         };
29392         /*
29393         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29394                     '<a href="{2}">' + 
29395                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29396                     '</a>' + 
29397                 '</div>',
29398         */
29399                 
29400         if (this.href.length > 0) {
29401             img = {
29402                 tag : 'a',
29403                 href: this.href,
29404                 contenteditable : 'true',
29405                 cn : [
29406                     img
29407                 ]
29408             };
29409         }
29410         
29411         
29412         if (this.video_url.length > 0) {
29413             img = {
29414                 tag : 'div',
29415                 cls : this.cls,
29416                 frameborder : 0,
29417                 allowfullscreen : true,
29418                 width : 420,  // these are for video tricks - that we replace the outer
29419                 height : 315,
29420                 src : this.video_url,
29421                 cn : [
29422                     img
29423                 ]
29424             };
29425         }
29426         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29427         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29428         
29429   
29430         var ret =   {
29431             tag: 'figure',
29432             'data-block' : 'Figure',
29433             'data-width' : this.width, 
29434             contenteditable : 'false',
29435             
29436             style : {
29437                 display: 'block',
29438                 float :  this.align ,
29439                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29440                 width : this.align == 'center' ? '100%' : this.width,
29441                 margin:  '0px',
29442                 padding: this.align == 'center' ? '0' : '0 10px' ,
29443                 textAlign : this.align   // seems to work for email..
29444                 
29445             },
29446            
29447             
29448             align : this.align,
29449             cn : [
29450                 img,
29451               
29452                 {
29453                     tag: 'figcaption',
29454                     'data-display' : this.caption_display,
29455                     style : {
29456                         textAlign : 'left',
29457                         fontSize : '16px',
29458                         lineHeight : '24px',
29459                         display : this.caption_display,
29460                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29461                         margin: m,
29462                         width: this.align == 'center' ?  this.width : '100%' 
29463                     
29464                          
29465                     },
29466                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29467                     cn : [
29468                         {
29469                             tag: 'div',
29470                             style  : {
29471                                 marginTop : '16px',
29472                                 textAlign : 'left'
29473                             },
29474                             align: 'left',
29475                             cn : [
29476                                 {
29477                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29478                                     tag : 'i',
29479                                     contenteditable : true,
29480                                     html : captionhtml
29481                                 }
29482                                 
29483                             ]
29484                         }
29485                         
29486                     ]
29487                     
29488                 }
29489             ]
29490         };
29491         return ret;
29492          
29493     },
29494     
29495     readElement : function(node)
29496     {
29497         // this should not really come from the link...
29498         this.video_url = this.getVal(node, 'div', 'src');
29499         this.cls = this.getVal(node, 'div', 'class');
29500         this.href = this.getVal(node, 'a', 'href');
29501         
29502         
29503         this.image_src = this.getVal(node, 'img', 'src');
29504          
29505         this.align = this.getVal(node, 'figure', 'align');
29506         var figcaption = this.getVal(node, 'figcaption', false);
29507         if (figcaption !== '') {
29508             this.caption = this.getVal(figcaption, 'i', 'html');
29509         }
29510         
29511
29512         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29513         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29514         this.width = this.getVal(node, true, 'data-width');
29515         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29516         
29517     },
29518     removeNode : function()
29519     {
29520         return this.node;
29521     }
29522     
29523   
29524    
29525      
29526     
29527     
29528     
29529     
29530 })
29531
29532  
29533
29534 /**
29535  * @class Roo.htmleditor.BlockTable
29536  * Block that manages a table
29537  * 
29538  * @constructor
29539  * Create a new Filter.
29540  * @param {Object} config Configuration options
29541  */
29542
29543 Roo.htmleditor.BlockTable = function(cfg)
29544 {
29545     if (cfg.node) {
29546         this.readElement(cfg.node);
29547         this.updateElement(cfg.node);
29548     }
29549     Roo.apply(this, cfg);
29550     if (!cfg.node) {
29551         this.rows = [];
29552         for(var r = 0; r < this.no_row; r++) {
29553             this.rows[r] = [];
29554             for(var c = 0; c < this.no_col; c++) {
29555                 this.rows[r][c] = this.emptyCell();
29556             }
29557         }
29558     }
29559     
29560     
29561 }
29562 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29563  
29564     rows : false,
29565     no_col : 1,
29566     no_row : 1,
29567     
29568     
29569     width: '100%',
29570     
29571     // used by context menu
29572     friendly_name : 'Table',
29573     deleteTitle : 'Delete Table',
29574     // context menu is drawn once..
29575     
29576     contextMenu : function(toolbar)
29577     {
29578         
29579         var block = function() {
29580             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29581         };
29582         
29583         
29584         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29585         
29586         var syncValue = toolbar.editorcore.syncValue;
29587         
29588         var fields = {};
29589         
29590         return [
29591             {
29592                 xtype : 'TextItem',
29593                 text : "Width: ",
29594                 xns : rooui.Toolbar  //Boostrap?
29595             },
29596             {
29597                 xtype : 'ComboBox',
29598                 allowBlank : false,
29599                 displayField : 'val',
29600                 editable : true,
29601                 listWidth : 100,
29602                 triggerAction : 'all',
29603                 typeAhead : true,
29604                 valueField : 'val',
29605                 width : 100,
29606                 name : 'width',
29607                 listeners : {
29608                     select : function (combo, r, index)
29609                     {
29610                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29611                         var b = block();
29612                         b.width = r.get('val');
29613                         b.updateElement();
29614                         syncValue();
29615                         toolbar.editorcore.onEditorEvent();
29616                     }
29617                 },
29618                 xns : rooui.form,
29619                 store : {
29620                     xtype : 'SimpleStore',
29621                     data : [
29622                         ['100%'],
29623                         ['auto']
29624                     ],
29625                     fields : [ 'val'],
29626                     xns : Roo.data
29627                 }
29628             },
29629             // -------- Cols
29630             
29631             {
29632                 xtype : 'TextItem',
29633                 text : "Columns: ",
29634                 xns : rooui.Toolbar  //Boostrap?
29635             },
29636          
29637             {
29638                 xtype : 'Button',
29639                 text: '-',
29640                 listeners : {
29641                     click : function (_self, e)
29642                     {
29643                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29644                         block().removeColumn();
29645                         syncValue();
29646                         toolbar.editorcore.onEditorEvent();
29647                     }
29648                 },
29649                 xns : rooui.Toolbar
29650             },
29651             {
29652                 xtype : 'Button',
29653                 text: '+',
29654                 listeners : {
29655                     click : function (_self, e)
29656                     {
29657                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29658                         block().addColumn();
29659                         syncValue();
29660                         toolbar.editorcore.onEditorEvent();
29661                     }
29662                 },
29663                 xns : rooui.Toolbar
29664             },
29665             // -------- ROWS
29666             {
29667                 xtype : 'TextItem',
29668                 text : "Rows: ",
29669                 xns : rooui.Toolbar  //Boostrap?
29670             },
29671          
29672             {
29673                 xtype : 'Button',
29674                 text: '-',
29675                 listeners : {
29676                     click : function (_self, e)
29677                     {
29678                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29679                         block().removeRow();
29680                         syncValue();
29681                         toolbar.editorcore.onEditorEvent();
29682                     }
29683                 },
29684                 xns : rooui.Toolbar
29685             },
29686             {
29687                 xtype : 'Button',
29688                 text: '+',
29689                 listeners : {
29690                     click : function (_self, e)
29691                     {
29692                         block().addRow();
29693                         syncValue();
29694                         toolbar.editorcore.onEditorEvent();
29695                     }
29696                 },
29697                 xns : rooui.Toolbar
29698             },
29699             // -------- ROWS
29700             {
29701                 xtype : 'Button',
29702                 text: 'Reset Column Widths',
29703                 listeners : {
29704                     
29705                     click : function (_self, e)
29706                     {
29707                         block().resetWidths();
29708                         syncValue();
29709                         toolbar.editorcore.onEditorEvent();
29710                     }
29711                 },
29712                 xns : rooui.Toolbar
29713             } 
29714             
29715             
29716             
29717         ];
29718         
29719     },
29720     
29721     
29722   /**
29723      * create a DomHelper friendly object - for use with
29724      * Roo.DomHelper.markup / overwrite / etc..
29725      * ?? should it be called with option to hide all editing features?
29726      */
29727     toObject : function()
29728     {
29729         
29730         var ret = {
29731             tag : 'table',
29732             contenteditable : 'false', // this stops cell selection from picking the table.
29733             'data-block' : 'Table',
29734             style : {
29735                 width:  this.width,
29736                 border : 'solid 1px #000', // ??? hard coded?
29737                 'border-collapse' : 'collapse' 
29738             },
29739             cn : [
29740                 { tag : 'tbody' , cn : [] }
29741             ]
29742         };
29743         
29744         // do we have a head = not really 
29745         var ncols = 0;
29746         Roo.each(this.rows, function( row ) {
29747             var tr = {
29748                 tag: 'tr',
29749                 style : {
29750                     margin: '6px',
29751                     border : 'solid 1px #000',
29752                     textAlign : 'left' 
29753                 },
29754                 cn : [ ]
29755             };
29756             
29757             ret.cn[0].cn.push(tr);
29758             // does the row have any properties? ?? height?
29759             var nc = 0;
29760             Roo.each(row, function( cell ) {
29761                 
29762                 var td = {
29763                     tag : 'td',
29764                     contenteditable :  'true',
29765                     'data-block' : 'Td',
29766                     html : cell.html,
29767                     style : cell.style
29768                 };
29769                 if (cell.colspan > 1) {
29770                     td.colspan = cell.colspan ;
29771                     nc += cell.colspan;
29772                 } else {
29773                     nc++;
29774                 }
29775                 if (cell.rowspan > 1) {
29776                     td.rowspan = cell.rowspan ;
29777                 }
29778                 
29779                 
29780                 // widths ?
29781                 tr.cn.push(td);
29782                     
29783                 
29784             }, this);
29785             ncols = Math.max(nc, ncols);
29786             
29787             
29788         }, this);
29789         // add the header row..
29790         
29791         ncols++;
29792          
29793         
29794         return ret;
29795          
29796     },
29797     
29798     readElement : function(node)
29799     {
29800         node  = node ? node : this.node ;
29801         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29802         
29803         this.rows = [];
29804         this.no_row = 0;
29805         var trs = Array.from(node.rows);
29806         trs.forEach(function(tr) {
29807             var row =  [];
29808             this.rows.push(row);
29809             
29810             this.no_row++;
29811             var no_column = 0;
29812             Array.from(tr.cells).forEach(function(td) {
29813                 
29814                 var add = {
29815                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29816                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29817                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29818                     html : td.innerHTML
29819                 };
29820                 no_column += add.colspan;
29821                      
29822                 
29823                 row.push(add);
29824                 
29825                 
29826             },this);
29827             this.no_col = Math.max(this.no_col, no_column);
29828             
29829             
29830         },this);
29831         
29832         
29833     },
29834     normalizeRows: function()
29835     {
29836         var ret= [];
29837         var rid = -1;
29838         this.rows.forEach(function(row) {
29839             rid++;
29840             ret[rid] = [];
29841             row = this.normalizeRow(row);
29842             var cid = 0;
29843             row.forEach(function(c) {
29844                 while (typeof(ret[rid][cid]) != 'undefined') {
29845                     cid++;
29846                 }
29847                 if (typeof(ret[rid]) == 'undefined') {
29848                     ret[rid] = [];
29849                 }
29850                 ret[rid][cid] = c;
29851                 c.row = rid;
29852                 c.col = cid;
29853                 if (c.rowspan < 2) {
29854                     return;
29855                 }
29856                 
29857                 for(var i = 1 ;i < c.rowspan; i++) {
29858                     if (typeof(ret[rid+i]) == 'undefined') {
29859                         ret[rid+i] = [];
29860                     }
29861                     ret[rid+i][cid] = c;
29862                 }
29863             });
29864         }, this);
29865         return ret;
29866     
29867     },
29868     
29869     normalizeRow: function(row)
29870     {
29871         var ret= [];
29872         row.forEach(function(c) {
29873             if (c.colspan < 2) {
29874                 ret.push(c);
29875                 return;
29876             }
29877             for(var i =0 ;i < c.colspan; i++) {
29878                 ret.push(c);
29879             }
29880         });
29881         return ret;
29882     
29883     },
29884     
29885     deleteColumn : function(sel)
29886     {
29887         if (!sel || sel.type != 'col') {
29888             return;
29889         }
29890         if (this.no_col < 2) {
29891             return;
29892         }
29893         
29894         this.rows.forEach(function(row) {
29895             var cols = this.normalizeRow(row);
29896             var col = cols[sel.col];
29897             if (col.colspan > 1) {
29898                 col.colspan --;
29899             } else {
29900                 row.remove(col);
29901             }
29902             
29903         }, this);
29904         this.no_col--;
29905         
29906     },
29907     removeColumn : function()
29908     {
29909         this.deleteColumn({
29910             type: 'col',
29911             col : this.no_col-1
29912         });
29913         this.updateElement();
29914     },
29915     
29916      
29917     addColumn : function()
29918     {
29919         
29920         this.rows.forEach(function(row) {
29921             row.push(this.emptyCell());
29922            
29923         }, this);
29924         this.updateElement();
29925     },
29926     
29927     deleteRow : function(sel)
29928     {
29929         if (!sel || sel.type != 'row') {
29930             return;
29931         }
29932         
29933         if (this.no_row < 2) {
29934             return;
29935         }
29936         
29937         var rows = this.normalizeRows();
29938         
29939         
29940         rows[sel.row].forEach(function(col) {
29941             if (col.rowspan > 1) {
29942                 col.rowspan--;
29943             } else {
29944                 col.remove = 1; // flage it as removed.
29945             }
29946             
29947         }, this);
29948         var newrows = [];
29949         this.rows.forEach(function(row) {
29950             newrow = [];
29951             row.forEach(function(c) {
29952                 if (typeof(c.remove) == 'undefined') {
29953                     newrow.push(c);
29954                 }
29955                 
29956             });
29957             if (newrow.length > 0) {
29958                 newrows.push(row);
29959             }
29960         });
29961         this.rows =  newrows;
29962         
29963         
29964         
29965         this.no_row--;
29966         this.updateElement();
29967         
29968     },
29969     removeRow : function()
29970     {
29971         this.deleteRow({
29972             type: 'row',
29973             row : this.no_row-1
29974         });
29975         
29976     },
29977     
29978      
29979     addRow : function()
29980     {
29981         
29982         var row = [];
29983         for (var i = 0; i < this.no_col; i++ ) {
29984             
29985             row.push(this.emptyCell());
29986            
29987         }
29988         this.rows.push(row);
29989         this.updateElement();
29990         
29991     },
29992      
29993     // the default cell object... at present...
29994     emptyCell : function() {
29995         return (new Roo.htmleditor.BlockTd({})).toObject();
29996         
29997      
29998     },
29999     
30000     removeNode : function()
30001     {
30002         return this.node;
30003     },
30004     
30005     
30006     
30007     resetWidths : function()
30008     {
30009         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30010             var nn = Roo.htmleditor.Block.factory(n);
30011             nn.width = '';
30012             nn.updateElement(n);
30013         });
30014     }
30015     
30016     
30017     
30018     
30019 })
30020
30021 /**
30022  *
30023  * editing a TD?
30024  *
30025  * since selections really work on the table cell, then editing really should work from there
30026  *
30027  * The original plan was to support merging etc... - but that may not be needed yet..
30028  *
30029  * So this simple version will support:
30030  *   add/remove cols
30031  *   adjust the width +/-
30032  *   reset the width...
30033  *   
30034  *
30035  */
30036
30037
30038  
30039
30040 /**
30041  * @class Roo.htmleditor.BlockTable
30042  * Block that manages a table
30043  * 
30044  * @constructor
30045  * Create a new Filter.
30046  * @param {Object} config Configuration options
30047  */
30048
30049 Roo.htmleditor.BlockTd = function(cfg)
30050 {
30051     if (cfg.node) {
30052         this.readElement(cfg.node);
30053         this.updateElement(cfg.node);
30054     }
30055     Roo.apply(this, cfg);
30056      
30057     
30058     
30059 }
30060 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30061  
30062     node : false,
30063     
30064     width: '',
30065     textAlign : 'left',
30066     valign : 'top',
30067     
30068     colspan : 1,
30069     rowspan : 1,
30070     
30071     
30072     // used by context menu
30073     friendly_name : 'Table Cell',
30074     deleteTitle : false, // use our customer delete
30075     
30076     // context menu is drawn once..
30077     
30078     contextMenu : function(toolbar)
30079     {
30080         
30081         var cell = function() {
30082             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30083         };
30084         
30085         var table = function() {
30086             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30087         };
30088         
30089         var lr = false;
30090         var saveSel = function()
30091         {
30092             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30093         }
30094         var restoreSel = function()
30095         {
30096             if (lr) {
30097                 (function() {
30098                     toolbar.editorcore.focus();
30099                     var cr = toolbar.editorcore.getSelection();
30100                     cr.removeAllRanges();
30101                     cr.addRange(lr);
30102                     toolbar.editorcore.onEditorEvent();
30103                 }).defer(10, this);
30104                 
30105                 
30106             }
30107         }
30108         
30109         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30110         
30111         var syncValue = toolbar.editorcore.syncValue;
30112         
30113         var fields = {};
30114         
30115         return [
30116             {
30117                 xtype : 'Button',
30118                 text : 'Edit Table',
30119                 listeners : {
30120                     click : function() {
30121                         var t = toolbar.tb.selectedNode.closest('table');
30122                         toolbar.editorcore.selectNode(t);
30123                         toolbar.editorcore.onEditorEvent();                        
30124                     }
30125                 }
30126                 
30127             },
30128               
30129            
30130              
30131             {
30132                 xtype : 'TextItem',
30133                 text : "Column Width: ",
30134                  xns : rooui.Toolbar 
30135                
30136             },
30137             {
30138                 xtype : 'Button',
30139                 text: '-',
30140                 listeners : {
30141                     click : function (_self, e)
30142                     {
30143                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30144                         cell().shrinkColumn();
30145                         syncValue();
30146                          toolbar.editorcore.onEditorEvent();
30147                     }
30148                 },
30149                 xns : rooui.Toolbar
30150             },
30151             {
30152                 xtype : 'Button',
30153                 text: '+',
30154                 listeners : {
30155                     click : function (_self, e)
30156                     {
30157                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30158                         cell().growColumn();
30159                         syncValue();
30160                         toolbar.editorcore.onEditorEvent();
30161                     }
30162                 },
30163                 xns : rooui.Toolbar
30164             },
30165             
30166             {
30167                 xtype : 'TextItem',
30168                 text : "Vertical Align: ",
30169                 xns : rooui.Toolbar  //Boostrap?
30170             },
30171             {
30172                 xtype : 'ComboBox',
30173                 allowBlank : false,
30174                 displayField : 'val',
30175                 editable : true,
30176                 listWidth : 100,
30177                 triggerAction : 'all',
30178                 typeAhead : true,
30179                 valueField : 'val',
30180                 width : 100,
30181                 name : 'valign',
30182                 listeners : {
30183                     select : function (combo, r, index)
30184                     {
30185                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30186                         var b = cell();
30187                         b.valign = r.get('val');
30188                         b.updateElement();
30189                         syncValue();
30190                         toolbar.editorcore.onEditorEvent();
30191                     }
30192                 },
30193                 xns : rooui.form,
30194                 store : {
30195                     xtype : 'SimpleStore',
30196                     data : [
30197                         ['top'],
30198                         ['middle'],
30199                         ['bottom'] // there are afew more... 
30200                     ],
30201                     fields : [ 'val'],
30202                     xns : Roo.data
30203                 }
30204             },
30205             
30206             {
30207                 xtype : 'TextItem',
30208                 text : "Merge Cells: ",
30209                  xns : rooui.Toolbar 
30210                
30211             },
30212             
30213             
30214             {
30215                 xtype : 'Button',
30216                 text: 'Right',
30217                 listeners : {
30218                     click : function (_self, e)
30219                     {
30220                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30221                         cell().mergeRight();
30222                         //block().growColumn();
30223                         syncValue();
30224                         toolbar.editorcore.onEditorEvent();
30225                     }
30226                 },
30227                 xns : rooui.Toolbar
30228             },
30229              
30230             {
30231                 xtype : 'Button',
30232                 text: 'Below',
30233                 listeners : {
30234                     click : function (_self, e)
30235                     {
30236                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30237                         cell().mergeBelow();
30238                         //block().growColumn();
30239                         syncValue();
30240                         toolbar.editorcore.onEditorEvent();
30241                     }
30242                 },
30243                 xns : rooui.Toolbar
30244             },
30245             {
30246                 xtype : 'TextItem',
30247                 text : "| ",
30248                  xns : rooui.Toolbar 
30249                
30250             },
30251             
30252             {
30253                 xtype : 'Button',
30254                 text: 'Split',
30255                 listeners : {
30256                     click : function (_self, e)
30257                     {
30258                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30259                         cell().split();
30260                         syncValue();
30261                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30262                         toolbar.editorcore.onEditorEvent();
30263                                              
30264                     }
30265                 },
30266                 xns : rooui.Toolbar
30267             },
30268             {
30269                 xtype : 'Fill',
30270                 xns : rooui.Toolbar 
30271                
30272             },
30273         
30274           
30275             {
30276                 xtype : 'Button',
30277                 text: 'Delete',
30278                  
30279                 xns : rooui.Toolbar,
30280                 menu : {
30281                     xtype : 'Menu',
30282                     xns : rooui.menu,
30283                     items : [
30284                         {
30285                             xtype : 'Item',
30286                             html: 'Column',
30287                             listeners : {
30288                                 click : function (_self, e)
30289                                 {
30290                                     var t = table();
30291                                     
30292                                     cell().deleteColumn();
30293                                     syncValue();
30294                                     toolbar.editorcore.selectNode(t.node);
30295                                     toolbar.editorcore.onEditorEvent();   
30296                                 }
30297                             },
30298                             xns : rooui.menu
30299                         },
30300                         {
30301                             xtype : 'Item',
30302                             html: 'Row',
30303                             listeners : {
30304                                 click : function (_self, e)
30305                                 {
30306                                     var t = table();
30307                                     cell().deleteRow();
30308                                     syncValue();
30309                                     
30310                                     toolbar.editorcore.selectNode(t.node);
30311                                     toolbar.editorcore.onEditorEvent();   
30312                                                          
30313                                 }
30314                             },
30315                             xns : rooui.menu
30316                         },
30317                        {
30318                             xtype : 'Separator',
30319                             xns : rooui.menu
30320                         },
30321                         {
30322                             xtype : 'Item',
30323                             html: 'Table',
30324                             listeners : {
30325                                 click : function (_self, e)
30326                                 {
30327                                     var t = table();
30328                                     var nn = t.node.nextSibling || t.node.previousSibling;
30329                                     t.node.parentNode.removeChild(t.node);
30330                                     if (nn) { 
30331                                         toolbar.editorcore.selectNode(nn, true);
30332                                     }
30333                                     toolbar.editorcore.onEditorEvent();   
30334                                                          
30335                                 }
30336                             },
30337                             xns : rooui.menu
30338                         }
30339                     ]
30340                 }
30341             }
30342             
30343             // align... << fixme
30344             
30345         ];
30346         
30347     },
30348     
30349     
30350   /**
30351      * create a DomHelper friendly object - for use with
30352      * Roo.DomHelper.markup / overwrite / etc..
30353      * ?? should it be called with option to hide all editing features?
30354      */
30355  /**
30356      * create a DomHelper friendly object - for use with
30357      * Roo.DomHelper.markup / overwrite / etc..
30358      * ?? should it be called with option to hide all editing features?
30359      */
30360     toObject : function()
30361     {
30362         var ret = {
30363             tag : 'td',
30364             contenteditable : 'true', // this stops cell selection from picking the table.
30365             'data-block' : 'Td',
30366             valign : this.valign,
30367             style : {  
30368                 'text-align' :  this.textAlign,
30369                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30370                 'border-collapse' : 'collapse',
30371                 padding : '6px', // 8 for desktop / 4 for mobile
30372                 'vertical-align': this.valign
30373             },
30374             html : this.html
30375         };
30376         if (this.width != '') {
30377             ret.width = this.width;
30378             ret.style.width = this.width;
30379         }
30380         
30381         
30382         if (this.colspan > 1) {
30383             ret.colspan = this.colspan ;
30384         } 
30385         if (this.rowspan > 1) {
30386             ret.rowspan = this.rowspan ;
30387         }
30388         
30389            
30390         
30391         return ret;
30392          
30393     },
30394     
30395     readElement : function(node)
30396     {
30397         node  = node ? node : this.node ;
30398         this.width = node.style.width;
30399         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30400         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30401         this.html = node.innerHTML;
30402         if (node.style.textAlign != '') {
30403             this.textAlign = node.style.textAlign;
30404         }
30405         
30406         
30407     },
30408      
30409     // the default cell object... at present...
30410     emptyCell : function() {
30411         return {
30412             colspan :  1,
30413             rowspan :  1,
30414             textAlign : 'left',
30415             html : "&nbsp;" // is this going to be editable now?
30416         };
30417      
30418     },
30419     
30420     removeNode : function()
30421     {
30422         return this.node.closest('table');
30423          
30424     },
30425     
30426     cellData : false,
30427     
30428     colWidths : false,
30429     
30430     toTableArray  : function()
30431     {
30432         var ret = [];
30433         var tab = this.node.closest('tr').closest('table');
30434         Array.from(tab.rows).forEach(function(r, ri){
30435             ret[ri] = [];
30436         });
30437         var rn = 0;
30438         this.colWidths = [];
30439         var all_auto = true;
30440         Array.from(tab.rows).forEach(function(r, ri){
30441             
30442             var cn = 0;
30443             Array.from(r.cells).forEach(function(ce, ci){
30444                 var c =  {
30445                     cell : ce,
30446                     row : rn,
30447                     col: cn,
30448                     colspan : ce.colSpan,
30449                     rowspan : ce.rowSpan
30450                 };
30451                 if (ce.isEqualNode(this.node)) {
30452                     this.cellData = c;
30453                 }
30454                 // if we have been filled up by a row?
30455                 if (typeof(ret[rn][cn]) != 'undefined') {
30456                     while(typeof(ret[rn][cn]) != 'undefined') {
30457                         cn++;
30458                     }
30459                     c.col = cn;
30460                 }
30461                 
30462                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30463                     this.colWidths[cn] =   ce.style.width;
30464                     if (this.colWidths[cn] != '') {
30465                         all_auto = false;
30466                     }
30467                 }
30468                 
30469                 
30470                 if (c.colspan < 2 && c.rowspan < 2 ) {
30471                     ret[rn][cn] = c;
30472                     cn++;
30473                     return;
30474                 }
30475                 for(var j = 0; j < c.rowspan; j++) {
30476                     if (typeof(ret[rn+j]) == 'undefined') {
30477                         continue; // we have a problem..
30478                     }
30479                     ret[rn+j][cn] = c;
30480                     for(var i = 0; i < c.colspan; i++) {
30481                         ret[rn+j][cn+i] = c;
30482                     }
30483                 }
30484                 
30485                 cn += c.colspan;
30486             }, this);
30487             rn++;
30488         }, this);
30489         
30490         // initalize widths.?
30491         // either all widths or no widths..
30492         if (all_auto) {
30493             this.colWidths[0] = false; // no widths flag.
30494         }
30495         
30496         
30497         return ret;
30498         
30499     },
30500     
30501     
30502     
30503     
30504     mergeRight: function()
30505     {
30506          
30507         // get the contents of the next cell along..
30508         var tr = this.node.closest('tr');
30509         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30510         if (i >= tr.childNodes.length - 1) {
30511             return; // no cells on right to merge with.
30512         }
30513         var table = this.toTableArray();
30514         
30515         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30516             return; // nothing right?
30517         }
30518         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30519         // right cell - must be same rowspan and on the same row.
30520         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30521             return; // right hand side is not same rowspan.
30522         }
30523         
30524         
30525         
30526         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30527         tr.removeChild(rc.cell);
30528         this.colspan += rc.colspan;
30529         this.node.setAttribute('colspan', this.colspan);
30530
30531         var table = this.toTableArray();
30532         this.normalizeWidths(table);
30533         this.updateWidths(table);
30534     },
30535     
30536     
30537     mergeBelow : function()
30538     {
30539         var table = this.toTableArray();
30540         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30541             return; // no row below
30542         }
30543         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30544             return; // nothing right?
30545         }
30546         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30547         
30548         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30549             return; // right hand side is not same rowspan.
30550         }
30551         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30552         rc.cell.parentNode.removeChild(rc.cell);
30553         this.rowspan += rc.rowspan;
30554         this.node.setAttribute('rowspan', this.rowspan);
30555     },
30556     
30557     split: function()
30558     {
30559         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30560             return;
30561         }
30562         var table = this.toTableArray();
30563         var cd = this.cellData;
30564         this.rowspan = 1;
30565         this.colspan = 1;
30566         
30567         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30568              
30569             
30570             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30571                 if (r == cd.row && c == cd.col) {
30572                     this.node.removeAttribute('rowspan');
30573                     this.node.removeAttribute('colspan');
30574                 }
30575                  
30576                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30577                 ntd.removeAttribute('id'); 
30578                 ntd.style.width  = this.colWidths[c];
30579                 ntd.innerHTML = '';
30580                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30581             }
30582             
30583         }
30584         this.redrawAllCells(table);
30585         
30586     },
30587     
30588     
30589     
30590     redrawAllCells: function(table)
30591     {
30592         
30593          
30594         var tab = this.node.closest('tr').closest('table');
30595         var ctr = tab.rows[0].parentNode;
30596         Array.from(tab.rows).forEach(function(r, ri){
30597             
30598             Array.from(r.cells).forEach(function(ce, ci){
30599                 ce.parentNode.removeChild(ce);
30600             });
30601             r.parentNode.removeChild(r);
30602         });
30603         for(var r = 0 ; r < table.length; r++) {
30604             var re = tab.rows[r];
30605             
30606             var re = tab.ownerDocument.createElement('tr');
30607             ctr.appendChild(re);
30608             for(var c = 0 ; c < table[r].length; c++) {
30609                 if (table[r][c].cell === false) {
30610                     continue;
30611                 }
30612                 
30613                 re.appendChild(table[r][c].cell);
30614                  
30615                 table[r][c].cell = false;
30616             }
30617         }
30618         
30619     },
30620     updateWidths : function(table)
30621     {
30622         for(var r = 0 ; r < table.length; r++) {
30623            
30624             for(var c = 0 ; c < table[r].length; c++) {
30625                 if (table[r][c].cell === false) {
30626                     continue;
30627                 }
30628                 
30629                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30630                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30631                     el.width = Math.floor(this.colWidths[c])  +'%';
30632                     el.updateElement(el.node);
30633                 }
30634                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30635                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30636                     var width = 0;
30637                     for(var i = 0; i < table[r][c].colspan; i ++) {
30638                         width += Math.floor(this.colWidths[c + i]);
30639                     }
30640                     el.width = width  +'%';
30641                     el.updateElement(el.node);
30642                 }
30643                 table[r][c].cell = false; // done
30644             }
30645         }
30646     },
30647     normalizeWidths : function(table)
30648     {
30649         if (this.colWidths[0] === false) {
30650             var nw = 100.0 / this.colWidths.length;
30651             this.colWidths.forEach(function(w,i) {
30652                 this.colWidths[i] = nw;
30653             },this);
30654             return;
30655         }
30656     
30657         var t = 0, missing = [];
30658         
30659         this.colWidths.forEach(function(w,i) {
30660             //if you mix % and
30661             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30662             var add =  this.colWidths[i];
30663             if (add > 0) {
30664                 t+=add;
30665                 return;
30666             }
30667             missing.push(i);
30668             
30669             
30670         },this);
30671         var nc = this.colWidths.length;
30672         if (missing.length) {
30673             var mult = (nc - missing.length) / (1.0 * nc);
30674             var t = mult * t;
30675             var ew = (100 -t) / (1.0 * missing.length);
30676             this.colWidths.forEach(function(w,i) {
30677                 if (w > 0) {
30678                     this.colWidths[i] = w * mult;
30679                     return;
30680                 }
30681                 
30682                 this.colWidths[i] = ew;
30683             }, this);
30684             // have to make up numbers..
30685              
30686         }
30687         // now we should have all the widths..
30688         
30689     
30690     },
30691     
30692     shrinkColumn : function()
30693     {
30694         var table = this.toTableArray();
30695         this.normalizeWidths(table);
30696         var col = this.cellData.col;
30697         var nw = this.colWidths[col] * 0.8;
30698         if (nw < 5) {
30699             return;
30700         }
30701         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30702         this.colWidths.forEach(function(w,i) {
30703             if (i == col) {
30704                  this.colWidths[i] = nw;
30705                 return;
30706             }
30707             this.colWidths[i] += otherAdd
30708         }, this);
30709         this.updateWidths(table);
30710          
30711     },
30712     growColumn : function()
30713     {
30714         var table = this.toTableArray();
30715         this.normalizeWidths(table);
30716         var col = this.cellData.col;
30717         var nw = this.colWidths[col] * 1.2;
30718         if (nw > 90) {
30719             return;
30720         }
30721         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30722         this.colWidths.forEach(function(w,i) {
30723             if (i == col) {
30724                 this.colWidths[i] = nw;
30725                 return;
30726             }
30727             this.colWidths[i] -= otherSub
30728         }, this);
30729         this.updateWidths(table);
30730          
30731     },
30732     deleteRow : function()
30733     {
30734         // delete this rows 'tr'
30735         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30736         // then reduce the rowspan.
30737         var table = this.toTableArray();
30738         // this.cellData.row;
30739         for (var i =0;i< table[this.cellData.row].length ; i++) {
30740             var c = table[this.cellData.row][i];
30741             if (c.row != this.cellData.row) {
30742                 
30743                 c.rowspan--;
30744                 c.cell.setAttribute('rowspan', c.rowspan);
30745                 continue;
30746             }
30747             if (c.rowspan > 1) {
30748                 c.rowspan--;
30749                 c.cell.setAttribute('rowspan', c.rowspan);
30750             }
30751         }
30752         table.splice(this.cellData.row,1);
30753         this.redrawAllCells(table);
30754         
30755     },
30756     deleteColumn : function()
30757     {
30758         var table = this.toTableArray();
30759         
30760         for (var i =0;i< table.length ; i++) {
30761             var c = table[i][this.cellData.col];
30762             if (c.col != this.cellData.col) {
30763                 table[i][this.cellData.col].colspan--;
30764             } else if (c.colspan > 1) {
30765                 c.colspan--;
30766                 c.cell.setAttribute('colspan', c.colspan);
30767             }
30768             table[i].splice(this.cellData.col,1);
30769         }
30770         
30771         this.redrawAllCells(table);
30772     }
30773     
30774     
30775     
30776     
30777 })
30778
30779 //<script type="text/javascript">
30780
30781 /*
30782  * Based  Ext JS Library 1.1.1
30783  * Copyright(c) 2006-2007, Ext JS, LLC.
30784  * LGPL
30785  *
30786  */
30787  
30788 /**
30789  * @class Roo.HtmlEditorCore
30790  * @extends Roo.Component
30791  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30792  *
30793  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30794  */
30795
30796 Roo.HtmlEditorCore = function(config){
30797     
30798     
30799     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30800     
30801     
30802     this.addEvents({
30803         /**
30804          * @event initialize
30805          * Fires when the editor is fully initialized (including the iframe)
30806          * @param {Roo.HtmlEditorCore} this
30807          */
30808         initialize: true,
30809         /**
30810          * @event activate
30811          * Fires when the editor is first receives the focus. Any insertion must wait
30812          * until after this event.
30813          * @param {Roo.HtmlEditorCore} this
30814          */
30815         activate: true,
30816          /**
30817          * @event beforesync
30818          * Fires before the textarea is updated with content from the editor iframe. Return false
30819          * to cancel the sync.
30820          * @param {Roo.HtmlEditorCore} this
30821          * @param {String} html
30822          */
30823         beforesync: true,
30824          /**
30825          * @event beforepush
30826          * Fires before the iframe editor is updated with content from the textarea. Return false
30827          * to cancel the push.
30828          * @param {Roo.HtmlEditorCore} this
30829          * @param {String} html
30830          */
30831         beforepush: true,
30832          /**
30833          * @event sync
30834          * Fires when the textarea is updated with content from the editor iframe.
30835          * @param {Roo.HtmlEditorCore} this
30836          * @param {String} html
30837          */
30838         sync: true,
30839          /**
30840          * @event push
30841          * Fires when the iframe editor is updated with content from the textarea.
30842          * @param {Roo.HtmlEditorCore} this
30843          * @param {String} html
30844          */
30845         push: true,
30846         
30847         /**
30848          * @event editorevent
30849          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30850          * @param {Roo.HtmlEditorCore} this
30851          */
30852         editorevent: true 
30853         
30854         
30855     });
30856     
30857     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30858     
30859     // defaults : white / black...
30860     this.applyBlacklists();
30861     
30862     
30863     
30864 };
30865
30866
30867 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30868
30869
30870      /**
30871      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30872      */
30873     
30874     owner : false,
30875     
30876      /**
30877      * @cfg {String} css styling for resizing. (used on bootstrap only)
30878      */
30879     resize : false,
30880      /**
30881      * @cfg {Number} height (in pixels)
30882      */   
30883     height: 300,
30884    /**
30885      * @cfg {Number} width (in pixels)
30886      */   
30887     width: 500,
30888      /**
30889      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30890      *         if you are doing an email editor, this probably needs disabling, it's designed
30891      */
30892     autoClean: true,
30893     
30894     /**
30895      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30896      */
30897     enableBlocks : true,
30898     /**
30899      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30900      * 
30901      */
30902     stylesheets: false,
30903      /**
30904      * @cfg {String} language default en - language of text (usefull for rtl languages)
30905      * 
30906      */
30907     language: 'en',
30908     
30909     /**
30910      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30911      *          - by default they are stripped - if you are editing email you may need this.
30912      */
30913     allowComments: false,
30914     // id of frame..
30915     frameId: false,
30916     
30917     // private properties
30918     validationEvent : false,
30919     deferHeight: true,
30920     initialized : false,
30921     activated : false,
30922     sourceEditMode : false,
30923     onFocus : Roo.emptyFn,
30924     iframePad:3,
30925     hideMode:'offsets',
30926     
30927     clearUp: true,
30928     
30929     // blacklist + whitelisted elements..
30930     black: false,
30931     white: false,
30932      
30933     bodyCls : '',
30934
30935     
30936     undoManager : false,
30937     /**
30938      * Protected method that will not generally be called directly. It
30939      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30940      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30941      */
30942     getDocMarkup : function(){
30943         // body styles..
30944         var st = '';
30945         
30946         // inherit styels from page...?? 
30947         if (this.stylesheets === false) {
30948             
30949             Roo.get(document.head).select('style').each(function(node) {
30950                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30951             });
30952             
30953             Roo.get(document.head).select('link').each(function(node) { 
30954                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30955             });
30956             
30957         } else if (!this.stylesheets.length) {
30958                 // simple..
30959                 st = '<style type="text/css">' +
30960                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30961                    '</style>';
30962         } else {
30963             for (var i in this.stylesheets) {
30964                 if (typeof(this.stylesheets[i]) != 'string') {
30965                     continue;
30966                 }
30967                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30968             }
30969             
30970         }
30971         
30972         st +=  '<style type="text/css">' +
30973             'IMG { cursor: pointer } ' +
30974         '</style>';
30975         
30976         st += '<meta name="google" content="notranslate">';
30977         
30978         var cls = 'notranslate roo-htmleditor-body';
30979         
30980         if(this.bodyCls.length){
30981             cls += ' ' + this.bodyCls;
30982         }
30983         
30984         return '<html  class="notranslate" translate="no"><head>' + st  +
30985             //<style type="text/css">' +
30986             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30987             //'</style>' +
30988             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30989     },
30990
30991     // private
30992     onRender : function(ct, position)
30993     {
30994         var _t = this;
30995         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30996         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30997         
30998         
30999         this.el.dom.style.border = '0 none';
31000         this.el.dom.setAttribute('tabIndex', -1);
31001         this.el.addClass('x-hidden hide');
31002         
31003         
31004         
31005         if(Roo.isIE){ // fix IE 1px bogus margin
31006             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31007         }
31008        
31009         
31010         this.frameId = Roo.id();
31011         
31012         var ifcfg = {
31013             tag: 'iframe',
31014             cls: 'form-control', // bootstrap..
31015             id: this.frameId,
31016             name: this.frameId,
31017             frameBorder : 'no',
31018             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
31019         };
31020         if (this.resize) {
31021             ifcfg.style = { resize : this.resize };
31022         }
31023         
31024         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
31025         
31026         
31027         this.iframe = iframe.dom;
31028
31029         this.assignDocWin();
31030         
31031         this.doc.designMode = 'on';
31032        
31033         this.doc.open();
31034         this.doc.write(this.getDocMarkup());
31035         this.doc.close();
31036
31037         
31038         var task = { // must defer to wait for browser to be ready
31039             run : function(){
31040                 //console.log("run task?" + this.doc.readyState);
31041                 this.assignDocWin();
31042                 if(this.doc.body || this.doc.readyState == 'complete'){
31043                     try {
31044                         this.doc.designMode="on";
31045                         
31046                     } catch (e) {
31047                         return;
31048                     }
31049                     Roo.TaskMgr.stop(task);
31050                     this.initEditor.defer(10, this);
31051                 }
31052             },
31053             interval : 10,
31054             duration: 10000,
31055             scope: this
31056         };
31057         Roo.TaskMgr.start(task);
31058
31059     },
31060
31061     // private
31062     onResize : function(w, h)
31063     {
31064          Roo.log('resize: ' +w + ',' + h );
31065         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31066         if(!this.iframe){
31067             return;
31068         }
31069         if(typeof w == 'number'){
31070             
31071             this.iframe.style.width = w + 'px';
31072         }
31073         if(typeof h == 'number'){
31074             
31075             this.iframe.style.height = h + 'px';
31076             if(this.doc){
31077                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31078             }
31079         }
31080         
31081     },
31082
31083     /**
31084      * Toggles the editor between standard and source edit mode.
31085      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31086      */
31087     toggleSourceEdit : function(sourceEditMode){
31088         
31089         this.sourceEditMode = sourceEditMode === true;
31090         
31091         if(this.sourceEditMode){
31092  
31093             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31094             
31095         }else{
31096             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31097             //this.iframe.className = '';
31098             this.deferFocus();
31099         }
31100         //this.setSize(this.owner.wrap.getSize());
31101         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31102     },
31103
31104     
31105   
31106
31107     /**
31108      * Protected method that will not generally be called directly. If you need/want
31109      * custom HTML cleanup, this is the method you should override.
31110      * @param {String} html The HTML to be cleaned
31111      * return {String} The cleaned HTML
31112      */
31113     cleanHtml : function(html)
31114     {
31115         html = String(html);
31116         if(html.length > 5){
31117             if(Roo.isSafari){ // strip safari nonsense
31118                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31119             }
31120         }
31121         if(html == '&nbsp;'){
31122             html = '';
31123         }
31124         return html;
31125     },
31126
31127     /**
31128      * HTML Editor -> Textarea
31129      * Protected method that will not generally be called directly. Syncs the contents
31130      * of the editor iframe with the textarea.
31131      */
31132     syncValue : function()
31133     {
31134         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31135         if(this.initialized){
31136             
31137             if (this.undoManager) {
31138                 this.undoManager.addEvent();
31139             }
31140
31141             
31142             var bd = (this.doc.body || this.doc.documentElement);
31143            
31144             
31145             var sel = this.win.getSelection();
31146             
31147             var div = document.createElement('div');
31148             div.innerHTML = bd.innerHTML;
31149             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31150             if (gtx.length > 0) {
31151                 var rm = gtx.item(0).parentNode;
31152                 rm.parentNode.removeChild(rm);
31153             }
31154             
31155            
31156             if (this.enableBlocks) {
31157                 new Roo.htmleditor.FilterBlock({ node : div });
31158             }
31159             
31160             var html = div.innerHTML;
31161             
31162             //?? tidy?
31163             if (this.autoClean) {
31164                 
31165                 new Roo.htmleditor.FilterAttributes({
31166                     node : div,
31167                     attrib_white : [
31168                             'href',
31169                             'src',
31170                             'name',
31171                             'align',
31172                             'colspan',
31173                             'rowspan',
31174                             'data-display',
31175                             'data-width',
31176                             'start' ,
31177                             'style',
31178                             // youtube embed.
31179                             'class',
31180                             'allowfullscreen',
31181                             'frameborder',
31182                             'width',
31183                             'height',
31184                             'alt'
31185                             ],
31186                     attrib_clean : ['href', 'src' ] 
31187                 });
31188                 
31189                 var tidy = new Roo.htmleditor.TidySerializer({
31190                     inner:  true
31191                 });
31192                 html  = tidy.serialize(div);
31193                 
31194             }
31195             
31196             
31197             if(Roo.isSafari){
31198                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31199                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31200                 if(m && m[1]){
31201                     html = '<div style="'+m[0]+'">' + html + '</div>';
31202                 }
31203             }
31204             html = this.cleanHtml(html);
31205             // fix up the special chars.. normaly like back quotes in word...
31206             // however we do not want to do this with chinese..
31207             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31208                 
31209                 var cc = match.charCodeAt();
31210
31211                 // Get the character value, handling surrogate pairs
31212                 if (match.length == 2) {
31213                     // It's a surrogate pair, calculate the Unicode code point
31214                     var high = match.charCodeAt(0) - 0xD800;
31215                     var low  = match.charCodeAt(1) - 0xDC00;
31216                     cc = (high * 0x400) + low + 0x10000;
31217                 }  else if (
31218                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31219                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31220                     (cc >= 0xf900 && cc < 0xfb00 )
31221                 ) {
31222                         return match;
31223                 }  
31224          
31225                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31226                 return "&#" + cc + ";";
31227                 
31228                 
31229             });
31230             
31231             
31232              
31233             if(this.owner.fireEvent('beforesync', this, html) !== false){
31234                 this.el.dom.value = html;
31235                 this.owner.fireEvent('sync', this, html);
31236             }
31237         }
31238     },
31239
31240     /**
31241      * TEXTAREA -> EDITABLE
31242      * Protected method that will not generally be called directly. Pushes the value of the textarea
31243      * into the iframe editor.
31244      */
31245     pushValue : function()
31246     {
31247         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31248         if(this.initialized){
31249             var v = this.el.dom.value.trim();
31250             
31251             
31252             if(this.owner.fireEvent('beforepush', this, v) !== false){
31253                 var d = (this.doc.body || this.doc.documentElement);
31254                 d.innerHTML = v;
31255                  
31256                 this.el.dom.value = d.innerHTML;
31257                 this.owner.fireEvent('push', this, v);
31258             }
31259             if (this.autoClean) {
31260                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31261                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31262             }
31263             if (this.enableBlocks) {
31264                 Roo.htmleditor.Block.initAll(this.doc.body);
31265             }
31266             
31267             this.updateLanguage();
31268             
31269             var lc = this.doc.body.lastChild;
31270             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31271                 // add an extra line at the end.
31272                 this.doc.body.appendChild(this.doc.createElement('br'));
31273             }
31274             
31275             
31276         }
31277     },
31278
31279     // private
31280     deferFocus : function(){
31281         this.focus.defer(10, this);
31282     },
31283
31284     // doc'ed in Field
31285     focus : function(){
31286         if(this.win && !this.sourceEditMode){
31287             this.win.focus();
31288         }else{
31289             this.el.focus();
31290         }
31291     },
31292     
31293     assignDocWin: function()
31294     {
31295         var iframe = this.iframe;
31296         
31297          if(Roo.isIE){
31298             this.doc = iframe.contentWindow.document;
31299             this.win = iframe.contentWindow;
31300         } else {
31301 //            if (!Roo.get(this.frameId)) {
31302 //                return;
31303 //            }
31304 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31305 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31306             
31307             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31308                 return;
31309             }
31310             
31311             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31312             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31313         }
31314     },
31315     
31316     // private
31317     initEditor : function(){
31318         //console.log("INIT EDITOR");
31319         this.assignDocWin();
31320         
31321         
31322         
31323         this.doc.designMode="on";
31324         this.doc.open();
31325         this.doc.write(this.getDocMarkup());
31326         this.doc.close();
31327         
31328         var dbody = (this.doc.body || this.doc.documentElement);
31329         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31330         // this copies styles from the containing element into thsi one..
31331         // not sure why we need all of this..
31332         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31333         
31334         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31335         //ss['background-attachment'] = 'fixed'; // w3c
31336         dbody.bgProperties = 'fixed'; // ie
31337         dbody.setAttribute("translate", "no");
31338         
31339         //Roo.DomHelper.applyStyles(dbody, ss);
31340         Roo.EventManager.on(this.doc, {
31341              
31342             'mouseup': this.onEditorEvent,
31343             'dblclick': this.onEditorEvent,
31344             'click': this.onEditorEvent,
31345             'keyup': this.onEditorEvent,
31346             
31347             buffer:100,
31348             scope: this
31349         });
31350         Roo.EventManager.on(this.doc, {
31351             'paste': this.onPasteEvent,
31352             scope : this
31353         });
31354         if(Roo.isGecko){
31355             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31356         }
31357         //??? needed???
31358         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31359             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31360         }
31361         this.initialized = true;
31362
31363         
31364         // initialize special key events - enter
31365         new Roo.htmleditor.KeyEnter({core : this});
31366         
31367          
31368         
31369         this.owner.fireEvent('initialize', this);
31370         this.pushValue();
31371     },
31372     // this is to prevent a href clicks resulting in a redirect?
31373    
31374     onPasteEvent : function(e,v)
31375     {
31376         // I think we better assume paste is going to be a dirty load of rubish from word..
31377         
31378         // even pasting into a 'email version' of this widget will have to clean up that mess.
31379         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31380         
31381         // check what type of paste - if it's an image, then handle it differently.
31382         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31383             // pasting images? 
31384             var urlAPI = (window.createObjectURL && window) || 
31385                 (window.URL && URL.revokeObjectURL && URL) || 
31386                 (window.webkitURL && webkitURL);
31387             
31388             var r = new FileReader();
31389             var t = this;
31390             r.addEventListener('load',function()
31391             {
31392                 
31393                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31394                 // is insert asycn?
31395                 if (t.enableBlocks) {
31396                     
31397                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31398                         if (img.closest('figure')) { // assume!! that it's aready
31399                             return;
31400                         }
31401                         var fig  = new Roo.htmleditor.BlockFigure({
31402                             image_src  : img.src
31403                         });
31404                         fig.updateElement(img); // replace it..
31405                         
31406                     });
31407                 }
31408                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31409                 t.owner.fireEvent('paste', this);
31410             });
31411             r.readAsDataURL(cd.files[0]);
31412             
31413             e.preventDefault();
31414             
31415             return false;
31416         }
31417         if (cd.types.indexOf('text/html') < 0 ) {
31418             return false;
31419         }
31420         var images = [];
31421         var html = cd.getData('text/html'); // clipboard event
31422         if (cd.types.indexOf('text/rtf') > -1) {
31423             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31424             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31425         }
31426         //Roo.log(images);
31427         //Roo.log(imgs);
31428         // fixme..
31429         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31430                        .map(function(g) { return g.toDataURL(); })
31431                        .filter(function(g) { return g != 'about:blank'; });
31432         
31433         //Roo.log(html);
31434         html = this.cleanWordChars(html);
31435         
31436         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31437         
31438         
31439         var sn = this.getParentElement();
31440         // check if d contains a table, and prevent nesting??
31441         //Roo.log(d.getElementsByTagName('table'));
31442         //Roo.log(sn);
31443         //Roo.log(sn.closest('table'));
31444         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31445             e.preventDefault();
31446             this.insertAtCursor("You can not nest tables");
31447             //Roo.log("prevent?"); // fixme - 
31448             return false;
31449         }
31450         
31451         
31452         
31453         if (images.length > 0) {
31454             // replace all v:imagedata - with img.
31455             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31456             Roo.each(ar, function(node) {
31457                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31458                 node.parentNode.removeChild(node);
31459             });
31460             
31461             
31462             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31463                 img.setAttribute('src', images[i]);
31464             });
31465         }
31466         if (this.autoClean) {
31467             new Roo.htmleditor.FilterWord({ node : d });
31468             
31469             new Roo.htmleditor.FilterStyleToTag({ node : d });
31470             new Roo.htmleditor.FilterAttributes({
31471                 node : d,
31472                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31473                 attrib_clean : ['href', 'src' ] 
31474             });
31475             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31476             // should be fonts..
31477             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31478             new Roo.htmleditor.FilterParagraph({ node : d });
31479             new Roo.htmleditor.FilterSpan({ node : d });
31480             new Roo.htmleditor.FilterLongBr({ node : d });
31481             new Roo.htmleditor.FilterComment({ node : d });
31482             
31483             
31484         }
31485         if (this.enableBlocks) {
31486                 
31487             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31488                 if (img.closest('figure')) { // assume!! that it's aready
31489                     return;
31490                 }
31491                 var fig  = new Roo.htmleditor.BlockFigure({
31492                     image_src  : img.src
31493                 });
31494                 fig.updateElement(img); // replace it..
31495                 
31496             });
31497         }
31498         
31499         
31500         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31501         if (this.enableBlocks) {
31502             Roo.htmleditor.Block.initAll(this.doc.body);
31503         }
31504          
31505         
31506         e.preventDefault();
31507         this.owner.fireEvent('paste', this);
31508         return false;
31509         // default behaveiour should be our local cleanup paste? (optional?)
31510         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31511         //this.owner.fireEvent('paste', e, v);
31512     },
31513     // private
31514     onDestroy : function(){
31515         
31516         
31517         
31518         if(this.rendered){
31519             
31520             //for (var i =0; i < this.toolbars.length;i++) {
31521             //    // fixme - ask toolbars for heights?
31522             //    this.toolbars[i].onDestroy();
31523            // }
31524             
31525             //this.wrap.dom.innerHTML = '';
31526             //this.wrap.remove();
31527         }
31528     },
31529
31530     // private
31531     onFirstFocus : function(){
31532         
31533         this.assignDocWin();
31534         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31535         
31536         this.activated = true;
31537          
31538     
31539         if(Roo.isGecko){ // prevent silly gecko errors
31540             this.win.focus();
31541             var s = this.win.getSelection();
31542             if(!s.focusNode || s.focusNode.nodeType != 3){
31543                 var r = s.getRangeAt(0);
31544                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31545                 r.collapse(true);
31546                 this.deferFocus();
31547             }
31548             try{
31549                 this.execCmd('useCSS', true);
31550                 this.execCmd('styleWithCSS', false);
31551             }catch(e){}
31552         }
31553         this.owner.fireEvent('activate', this);
31554     },
31555
31556     // private
31557     adjustFont: function(btn){
31558         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31559         //if(Roo.isSafari){ // safari
31560         //    adjust *= 2;
31561        // }
31562         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31563         if(Roo.isSafari){ // safari
31564             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31565             v =  (v < 10) ? 10 : v;
31566             v =  (v > 48) ? 48 : v;
31567             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31568             
31569         }
31570         
31571         
31572         v = Math.max(1, v+adjust);
31573         
31574         this.execCmd('FontSize', v  );
31575     },
31576
31577     onEditorEvent : function(e)
31578     {
31579          
31580         
31581         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31582             return; // we do not handle this.. (undo manager does..)
31583         }
31584         // clicking a 'block'?
31585         
31586         // in theory this detects if the last element is not a br, then we try and do that.
31587         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31588         if (e &&
31589             e.target.nodeName == 'BODY' &&
31590             e.type == "mouseup" &&
31591             this.doc.body.lastChild
31592            ) {
31593             var lc = this.doc.body.lastChild;
31594             // gtx-trans is google translate plugin adding crap.
31595             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31596                 lc = lc.previousSibling;
31597             }
31598             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31599             // if last element is <BR> - then dont do anything.
31600             
31601                 var ns = this.doc.createElement('br');
31602                 this.doc.body.appendChild(ns);
31603                 range = this.doc.createRange();
31604                 range.setStartAfter(ns);
31605                 range.collapse(true);
31606                 var sel = this.win.getSelection();
31607                 sel.removeAllRanges();
31608                 sel.addRange(range);
31609             }
31610         }
31611         
31612         
31613         
31614         this.fireEditorEvent(e);
31615       //  this.updateToolbar();
31616         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31617     },
31618     
31619     fireEditorEvent: function(e)
31620     {
31621         this.owner.fireEvent('editorevent', this, e);
31622     },
31623
31624     insertTag : function(tg)
31625     {
31626         // could be a bit smarter... -> wrap the current selected tRoo..
31627         if (tg.toLowerCase() == 'span' ||
31628             tg.toLowerCase() == 'code' ||
31629             tg.toLowerCase() == 'sup' ||
31630             tg.toLowerCase() == 'sub' 
31631             ) {
31632             
31633             range = this.createRange(this.getSelection());
31634             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31635             wrappingNode.appendChild(range.extractContents());
31636             range.insertNode(wrappingNode);
31637
31638             return;
31639             
31640             
31641             
31642         }
31643         this.execCmd("formatblock",   tg);
31644         this.undoManager.addEvent(); 
31645     },
31646     
31647     insertText : function(txt)
31648     {
31649         
31650         
31651         var range = this.createRange();
31652         range.deleteContents();
31653                //alert(Sender.getAttribute('label'));
31654                
31655         range.insertNode(this.doc.createTextNode(txt));
31656         this.undoManager.addEvent();
31657     } ,
31658     
31659      
31660
31661     /**
31662      * Executes a Midas editor command on the editor document and performs necessary focus and
31663      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31664      * @param {String} cmd The Midas command
31665      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31666      */
31667     relayCmd : function(cmd, value)
31668     {
31669         
31670         switch (cmd) {
31671             case 'justifyleft':
31672             case 'justifyright':
31673             case 'justifycenter':
31674                 // if we are in a cell, then we will adjust the
31675                 var n = this.getParentElement();
31676                 var td = n.closest('td');
31677                 if (td) {
31678                     var bl = Roo.htmleditor.Block.factory(td);
31679                     bl.textAlign = cmd.replace('justify','');
31680                     bl.updateElement();
31681                     this.owner.fireEvent('editorevent', this);
31682                     return;
31683                 }
31684                 this.execCmd('styleWithCSS', true); // 
31685                 break;
31686             case 'bold':
31687             case 'italic':
31688             case 'underline':                
31689                 // if there is no selection, then we insert, and set the curson inside it..
31690                 this.execCmd('styleWithCSS', false); 
31691                 break;
31692                 
31693         
31694             default:
31695                 break;
31696         }
31697         
31698         
31699         this.win.focus();
31700         this.execCmd(cmd, value);
31701         this.owner.fireEvent('editorevent', this);
31702         //this.updateToolbar();
31703         this.owner.deferFocus();
31704     },
31705
31706     /**
31707      * Executes a Midas editor command directly on the editor document.
31708      * For visual commands, you should use {@link #relayCmd} instead.
31709      * <b>This should only be called after the editor is initialized.</b>
31710      * @param {String} cmd The Midas command
31711      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31712      */
31713     execCmd : function(cmd, value){
31714         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31715         this.syncValue();
31716     },
31717  
31718  
31719    
31720     /**
31721      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31722      * to insert tRoo.
31723      * @param {String} text | dom node.. 
31724      */
31725     insertAtCursor : function(text)
31726     {
31727         
31728         if(!this.activated){
31729             return;
31730         }
31731          
31732         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31733             this.win.focus();
31734             
31735             
31736             // from jquery ui (MIT licenced)
31737             var range, node;
31738             var win = this.win;
31739             
31740             if (win.getSelection && win.getSelection().getRangeAt) {
31741                 
31742                 // delete the existing?
31743                 
31744                 this.createRange(this.getSelection()).deleteContents();
31745                 range = win.getSelection().getRangeAt(0);
31746                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31747                 range.insertNode(node);
31748                 range = range.cloneRange();
31749                 range.collapse(false);
31750                  
31751                 win.getSelection().removeAllRanges();
31752                 win.getSelection().addRange(range);
31753                 
31754                 
31755                 
31756             } else if (win.document.selection && win.document.selection.createRange) {
31757                 // no firefox support
31758                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31759                 win.document.selection.createRange().pasteHTML(txt);
31760             
31761             } else {
31762                 // no firefox support
31763                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31764                 this.execCmd('InsertHTML', txt);
31765             } 
31766             this.syncValue();
31767             
31768             this.deferFocus();
31769         }
31770     },
31771  // private
31772     mozKeyPress : function(e){
31773         if(e.ctrlKey){
31774             var c = e.getCharCode(), cmd;
31775           
31776             if(c > 0){
31777                 c = String.fromCharCode(c).toLowerCase();
31778                 switch(c){
31779                     case 'b':
31780                         cmd = 'bold';
31781                         break;
31782                     case 'i':
31783                         cmd = 'italic';
31784                         break;
31785                     
31786                     case 'u':
31787                         cmd = 'underline';
31788                         break;
31789                     
31790                     //case 'v':
31791                       //  this.cleanUpPaste.defer(100, this);
31792                       //  return;
31793                         
31794                 }
31795                 if(cmd){
31796                     
31797                     this.relayCmd(cmd);
31798                     //this.win.focus();
31799                     //this.execCmd(cmd);
31800                     //this.deferFocus();
31801                     e.preventDefault();
31802                 }
31803                 
31804             }
31805         }
31806     },
31807
31808     // private
31809     fixKeys : function(){ // load time branching for fastest keydown performance
31810         
31811         
31812         if(Roo.isIE){
31813             return function(e){
31814                 var k = e.getKey(), r;
31815                 if(k == e.TAB){
31816                     e.stopEvent();
31817                     r = this.doc.selection.createRange();
31818                     if(r){
31819                         r.collapse(true);
31820                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31821                         this.deferFocus();
31822                     }
31823                     return;
31824                 }
31825                 /// this is handled by Roo.htmleditor.KeyEnter
31826                  /*
31827                 if(k == e.ENTER){
31828                     r = this.doc.selection.createRange();
31829                     if(r){
31830                         var target = r.parentElement();
31831                         if(!target || target.tagName.toLowerCase() != 'li'){
31832                             e.stopEvent();
31833                             r.pasteHTML('<br/>');
31834                             r.collapse(false);
31835                             r.select();
31836                         }
31837                     }
31838                 }
31839                 */
31840                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31841                 //    this.cleanUpPaste.defer(100, this);
31842                 //    return;
31843                 //}
31844                 
31845                 
31846             };
31847         }else if(Roo.isOpera){
31848             return function(e){
31849                 var k = e.getKey();
31850                 if(k == e.TAB){
31851                     e.stopEvent();
31852                     this.win.focus();
31853                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31854                     this.deferFocus();
31855                 }
31856                
31857                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31858                 //    this.cleanUpPaste.defer(100, this);
31859                  //   return;
31860                 //}
31861                 
31862             };
31863         }else if(Roo.isSafari){
31864             return function(e){
31865                 var k = e.getKey();
31866                 
31867                 if(k == e.TAB){
31868                     e.stopEvent();
31869                     this.execCmd('InsertText','\t');
31870                     this.deferFocus();
31871                     return;
31872                 }
31873                  this.mozKeyPress(e);
31874                 
31875                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31876                  //   this.cleanUpPaste.defer(100, this);
31877                  //   return;
31878                // }
31879                 
31880              };
31881         }
31882     }(),
31883     
31884     getAllAncestors: function()
31885     {
31886         var p = this.getSelectedNode();
31887         var a = [];
31888         if (!p) {
31889             a.push(p); // push blank onto stack..
31890             p = this.getParentElement();
31891         }
31892         
31893         
31894         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31895             a.push(p);
31896             p = p.parentNode;
31897         }
31898         a.push(this.doc.body);
31899         return a;
31900     },
31901     lastSel : false,
31902     lastSelNode : false,
31903     
31904     
31905     getSelection : function() 
31906     {
31907         this.assignDocWin();
31908         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31909     },
31910     /**
31911      * Select a dom node
31912      * @param {DomElement} node the node to select
31913      */
31914     selectNode : function(node, collapse)
31915     {
31916         var nodeRange = node.ownerDocument.createRange();
31917         try {
31918             nodeRange.selectNode(node);
31919         } catch (e) {
31920             nodeRange.selectNodeContents(node);
31921         }
31922         if (collapse === true) {
31923             nodeRange.collapse(true);
31924         }
31925         //
31926         var s = this.win.getSelection();
31927         s.removeAllRanges();
31928         s.addRange(nodeRange);
31929     },
31930     
31931     getSelectedNode: function() 
31932     {
31933         // this may only work on Gecko!!!
31934         
31935         // should we cache this!!!!
31936         
31937          
31938          
31939         var range = this.createRange(this.getSelection()).cloneRange();
31940         
31941         if (Roo.isIE) {
31942             var parent = range.parentElement();
31943             while (true) {
31944                 var testRange = range.duplicate();
31945                 testRange.moveToElementText(parent);
31946                 if (testRange.inRange(range)) {
31947                     break;
31948                 }
31949                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31950                     break;
31951                 }
31952                 parent = parent.parentElement;
31953             }
31954             return parent;
31955         }
31956         
31957         // is ancestor a text element.
31958         var ac =  range.commonAncestorContainer;
31959         if (ac.nodeType == 3) {
31960             ac = ac.parentNode;
31961         }
31962         
31963         var ar = ac.childNodes;
31964          
31965         var nodes = [];
31966         var other_nodes = [];
31967         var has_other_nodes = false;
31968         for (var i=0;i<ar.length;i++) {
31969             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31970                 continue;
31971             }
31972             // fullly contained node.
31973             
31974             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31975                 nodes.push(ar[i]);
31976                 continue;
31977             }
31978             
31979             // probably selected..
31980             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31981                 other_nodes.push(ar[i]);
31982                 continue;
31983             }
31984             // outer..
31985             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31986                 continue;
31987             }
31988             
31989             
31990             has_other_nodes = true;
31991         }
31992         if (!nodes.length && other_nodes.length) {
31993             nodes= other_nodes;
31994         }
31995         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31996             return false;
31997         }
31998         
31999         return nodes[0];
32000     },
32001     
32002     
32003     createRange: function(sel)
32004     {
32005         // this has strange effects when using with 
32006         // top toolbar - not sure if it's a great idea.
32007         //this.editor.contentWindow.focus();
32008         if (typeof sel != "undefined") {
32009             try {
32010                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32011             } catch(e) {
32012                 return this.doc.createRange();
32013             }
32014         } else {
32015             return this.doc.createRange();
32016         }
32017     },
32018     getParentElement: function()
32019     {
32020         
32021         this.assignDocWin();
32022         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32023         
32024         var range = this.createRange(sel);
32025          
32026         try {
32027             var p = range.commonAncestorContainer;
32028             while (p.nodeType == 3) { // text node
32029                 p = p.parentNode;
32030             }
32031             return p;
32032         } catch (e) {
32033             return null;
32034         }
32035     
32036     },
32037     /***
32038      *
32039      * Range intersection.. the hard stuff...
32040      *  '-1' = before
32041      *  '0' = hits..
32042      *  '1' = after.
32043      *         [ -- selected range --- ]
32044      *   [fail]                        [fail]
32045      *
32046      *    basically..
32047      *      if end is before start or  hits it. fail.
32048      *      if start is after end or hits it fail.
32049      *
32050      *   if either hits (but other is outside. - then it's not 
32051      *   
32052      *    
32053      **/
32054     
32055     
32056     // @see http://www.thismuchiknow.co.uk/?p=64.
32057     rangeIntersectsNode : function(range, node)
32058     {
32059         var nodeRange = node.ownerDocument.createRange();
32060         try {
32061             nodeRange.selectNode(node);
32062         } catch (e) {
32063             nodeRange.selectNodeContents(node);
32064         }
32065     
32066         var rangeStartRange = range.cloneRange();
32067         rangeStartRange.collapse(true);
32068     
32069         var rangeEndRange = range.cloneRange();
32070         rangeEndRange.collapse(false);
32071     
32072         var nodeStartRange = nodeRange.cloneRange();
32073         nodeStartRange.collapse(true);
32074     
32075         var nodeEndRange = nodeRange.cloneRange();
32076         nodeEndRange.collapse(false);
32077     
32078         return rangeStartRange.compareBoundaryPoints(
32079                  Range.START_TO_START, nodeEndRange) == -1 &&
32080                rangeEndRange.compareBoundaryPoints(
32081                  Range.START_TO_START, nodeStartRange) == 1;
32082         
32083          
32084     },
32085     rangeCompareNode : function(range, node)
32086     {
32087         var nodeRange = node.ownerDocument.createRange();
32088         try {
32089             nodeRange.selectNode(node);
32090         } catch (e) {
32091             nodeRange.selectNodeContents(node);
32092         }
32093         
32094         
32095         range.collapse(true);
32096     
32097         nodeRange.collapse(true);
32098      
32099         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32100         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32101          
32102         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32103         
32104         var nodeIsBefore   =  ss == 1;
32105         var nodeIsAfter    = ee == -1;
32106         
32107         if (nodeIsBefore && nodeIsAfter) {
32108             return 0; // outer
32109         }
32110         if (!nodeIsBefore && nodeIsAfter) {
32111             return 1; //right trailed.
32112         }
32113         
32114         if (nodeIsBefore && !nodeIsAfter) {
32115             return 2;  // left trailed.
32116         }
32117         // fully contined.
32118         return 3;
32119     },
32120  
32121     cleanWordChars : function(input) {// change the chars to hex code
32122         
32123        var swapCodes  = [ 
32124             [    8211, "&#8211;" ], 
32125             [    8212, "&#8212;" ], 
32126             [    8216,  "'" ],  
32127             [    8217, "'" ],  
32128             [    8220, '"' ],  
32129             [    8221, '"' ],  
32130             [    8226, "*" ],  
32131             [    8230, "..." ]
32132         ]; 
32133         var output = input;
32134         Roo.each(swapCodes, function(sw) { 
32135             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32136             
32137             output = output.replace(swapper, sw[1]);
32138         });
32139         
32140         return output;
32141     },
32142     
32143      
32144     
32145         
32146     
32147     cleanUpChild : function (node)
32148     {
32149         
32150         new Roo.htmleditor.FilterComment({node : node});
32151         new Roo.htmleditor.FilterAttributes({
32152                 node : node,
32153                 attrib_black : this.ablack,
32154                 attrib_clean : this.aclean,
32155                 style_white : this.cwhite,
32156                 style_black : this.cblack
32157         });
32158         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32159         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32160          
32161         
32162     },
32163     
32164     /**
32165      * Clean up MS wordisms...
32166      * @deprecated - use filter directly
32167      */
32168     cleanWord : function(node)
32169     {
32170         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32171         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32172         
32173     },
32174    
32175     
32176     /**
32177
32178      * @deprecated - use filters
32179      */
32180     cleanTableWidths : function(node)
32181     {
32182         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32183         
32184  
32185     },
32186     
32187      
32188         
32189     applyBlacklists : function()
32190     {
32191         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32192         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32193         
32194         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32195         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32196         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32197         
32198         this.white = [];
32199         this.black = [];
32200         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32201             if (b.indexOf(tag) > -1) {
32202                 return;
32203             }
32204             this.white.push(tag);
32205             
32206         }, this);
32207         
32208         Roo.each(w, function(tag) {
32209             if (b.indexOf(tag) > -1) {
32210                 return;
32211             }
32212             if (this.white.indexOf(tag) > -1) {
32213                 return;
32214             }
32215             this.white.push(tag);
32216             
32217         }, this);
32218         
32219         
32220         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32221             if (w.indexOf(tag) > -1) {
32222                 return;
32223             }
32224             this.black.push(tag);
32225             
32226         }, this);
32227         
32228         Roo.each(b, function(tag) {
32229             if (w.indexOf(tag) > -1) {
32230                 return;
32231             }
32232             if (this.black.indexOf(tag) > -1) {
32233                 return;
32234             }
32235             this.black.push(tag);
32236             
32237         }, this);
32238         
32239         
32240         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32241         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32242         
32243         this.cwhite = [];
32244         this.cblack = [];
32245         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32246             if (b.indexOf(tag) > -1) {
32247                 return;
32248             }
32249             this.cwhite.push(tag);
32250             
32251         }, this);
32252         
32253         Roo.each(w, function(tag) {
32254             if (b.indexOf(tag) > -1) {
32255                 return;
32256             }
32257             if (this.cwhite.indexOf(tag) > -1) {
32258                 return;
32259             }
32260             this.cwhite.push(tag);
32261             
32262         }, this);
32263         
32264         
32265         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32266             if (w.indexOf(tag) > -1) {
32267                 return;
32268             }
32269             this.cblack.push(tag);
32270             
32271         }, this);
32272         
32273         Roo.each(b, function(tag) {
32274             if (w.indexOf(tag) > -1) {
32275                 return;
32276             }
32277             if (this.cblack.indexOf(tag) > -1) {
32278                 return;
32279             }
32280             this.cblack.push(tag);
32281             
32282         }, this);
32283     },
32284     
32285     setStylesheets : function(stylesheets)
32286     {
32287         if(typeof(stylesheets) == 'string'){
32288             Roo.get(this.iframe.contentDocument.head).createChild({
32289                 tag : 'link',
32290                 rel : 'stylesheet',
32291                 type : 'text/css',
32292                 href : stylesheets
32293             });
32294             
32295             return;
32296         }
32297         var _this = this;
32298      
32299         Roo.each(stylesheets, function(s) {
32300             if(!s.length){
32301                 return;
32302             }
32303             
32304             Roo.get(_this.iframe.contentDocument.head).createChild({
32305                 tag : 'link',
32306                 rel : 'stylesheet',
32307                 type : 'text/css',
32308                 href : s
32309             });
32310         });
32311
32312         
32313     },
32314     
32315     
32316     updateLanguage : function()
32317     {
32318         if (!this.iframe || !this.iframe.contentDocument) {
32319             return;
32320         }
32321         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32322     },
32323     
32324     
32325     removeStylesheets : function()
32326     {
32327         var _this = this;
32328         
32329         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32330             s.remove();
32331         });
32332     },
32333     
32334     setStyle : function(style)
32335     {
32336         Roo.get(this.iframe.contentDocument.head).createChild({
32337             tag : 'style',
32338             type : 'text/css',
32339             html : style
32340         });
32341
32342         return;
32343     }
32344     
32345     // hide stuff that is not compatible
32346     /**
32347      * @event blur
32348      * @hide
32349      */
32350     /**
32351      * @event change
32352      * @hide
32353      */
32354     /**
32355      * @event focus
32356      * @hide
32357      */
32358     /**
32359      * @event specialkey
32360      * @hide
32361      */
32362     /**
32363      * @cfg {String} fieldClass @hide
32364      */
32365     /**
32366      * @cfg {String} focusClass @hide
32367      */
32368     /**
32369      * @cfg {String} autoCreate @hide
32370      */
32371     /**
32372      * @cfg {String} inputType @hide
32373      */
32374     /**
32375      * @cfg {String} invalidClass @hide
32376      */
32377     /**
32378      * @cfg {String} invalidText @hide
32379      */
32380     /**
32381      * @cfg {String} msgFx @hide
32382      */
32383     /**
32384      * @cfg {String} validateOnBlur @hide
32385      */
32386 });
32387
32388 Roo.HtmlEditorCore.white = [
32389         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32390         
32391        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32392        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32393        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32394        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32395        'TABLE',   'UL',         'XMP', 
32396        
32397        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32398       'THEAD',   'TR', 
32399      
32400       'DIR', 'MENU', 'OL', 'UL', 'DL',
32401        
32402       'EMBED',  'OBJECT'
32403 ];
32404
32405
32406 Roo.HtmlEditorCore.black = [
32407     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32408         'APPLET', // 
32409         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32410         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32411         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32412         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32413         //'FONT' // CLEAN LATER..
32414         'COLGROUP', 'COL'   // messy tables.
32415         
32416         
32417 ];
32418 Roo.HtmlEditorCore.clean = [ // ?? needed???
32419      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32420 ];
32421 Roo.HtmlEditorCore.tag_remove = [
32422     'FONT', 'TBODY'  
32423 ];
32424 // attributes..
32425
32426 Roo.HtmlEditorCore.ablack = [
32427     'on'
32428 ];
32429     
32430 Roo.HtmlEditorCore.aclean = [ 
32431     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32432 ];
32433
32434 // protocols..
32435 Roo.HtmlEditorCore.pwhite= [
32436         'http',  'https',  'mailto'
32437 ];
32438
32439 // white listed style attributes.
32440 Roo.HtmlEditorCore.cwhite= [
32441       //  'text-align', /// default is to allow most things..
32442       
32443          
32444 //        'font-size'//??
32445 ];
32446
32447 // black listed style attributes.
32448 Roo.HtmlEditorCore.cblack= [
32449       //  'font-size' -- this can be set by the project 
32450 ];
32451
32452
32453
32454
32455     /*
32456  * - LGPL
32457  *
32458  * HtmlEditor
32459  * 
32460  */
32461
32462 /**
32463  * @class Roo.bootstrap.form.HtmlEditor
32464  * @extends Roo.bootstrap.form.TextArea
32465  * Bootstrap HtmlEditor class
32466
32467  * @constructor
32468  * Create a new HtmlEditor
32469  * @param {Object} config The config object
32470  */
32471
32472 Roo.bootstrap.form.HtmlEditor = function(config){
32473
32474     this.addEvents({
32475             /**
32476              * @event initialize
32477              * Fires when the editor is fully initialized (including the iframe)
32478              * @param {Roo.bootstrap.form.HtmlEditor} this
32479              */
32480             initialize: true,
32481             /**
32482              * @event activate
32483              * Fires when the editor is first receives the focus. Any insertion must wait
32484              * until after this event.
32485              * @param {Roo.bootstrap.form.HtmlEditor} this
32486              */
32487             activate: true,
32488              /**
32489              * @event beforesync
32490              * Fires before the textarea is updated with content from the editor iframe. Return false
32491              * to cancel the sync.
32492              * @param {Roo.bootstrap.form.HtmlEditor} this
32493              * @param {String} html
32494              */
32495             beforesync: true,
32496              /**
32497              * @event beforepush
32498              * Fires before the iframe editor is updated with content from the textarea. Return false
32499              * to cancel the push.
32500              * @param {Roo.bootstrap.form.HtmlEditor} this
32501              * @param {String} html
32502              */
32503             beforepush: true,
32504              /**
32505              * @event sync
32506              * Fires when the textarea is updated with content from the editor iframe.
32507              * @param {Roo.bootstrap.form.HtmlEditor} this
32508              * @param {String} html
32509              */
32510             sync: true,
32511              /**
32512              * @event push
32513              * Fires when the iframe editor is updated with content from the textarea.
32514              * @param {Roo.bootstrap.form.HtmlEditor} this
32515              * @param {String} html
32516              */
32517             push: true,
32518              /**
32519              * @event editmodechange
32520              * Fires when the editor switches edit modes
32521              * @param {Roo.bootstrap.form.HtmlEditor} this
32522              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32523              */
32524             editmodechange: true,
32525             /**
32526              * @event editorevent
32527              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32528              * @param {Roo.bootstrap.form.HtmlEditor} this
32529              */
32530             editorevent: true,
32531             /**
32532              * @event firstfocus
32533              * Fires when on first focus - needed by toolbars..
32534              * @param {Roo.bootstrap.form.HtmlEditor} this
32535              */
32536             firstfocus: true,
32537             /**
32538              * @event autosave
32539              * Auto save the htmlEditor value as a file into Events
32540              * @param {Roo.bootstrap.form.HtmlEditor} this
32541              */
32542             autosave: true,
32543             /**
32544              * @event savedpreview
32545              * preview the saved version of htmlEditor
32546              * @param {Roo.bootstrap.form.HtmlEditor} this
32547              */
32548             savedpreview: true,
32549              /**
32550             * @event stylesheetsclick
32551             * Fires when press the Sytlesheets button
32552             * @param {Roo.HtmlEditorCore} this
32553             */
32554             stylesheetsclick: true,
32555             /**
32556             * @event paste
32557             * Fires when press user pastes into the editor
32558             * @param {Roo.HtmlEditorCore} this
32559             */
32560             paste: true,
32561             /**
32562             * @event imageadd
32563             * Fires when on any editor when an image is added (excluding paste)
32564             * @param {Roo.bootstrap.form.HtmlEditor} this
32565             */
32566            imageadd: true ,
32567             /**
32568             * @event imageupdated
32569             * Fires when on any editor when an image is changed (excluding paste)
32570             * @param {Roo.bootstrap.form.HtmlEditor} this
32571             * @param {HTMLElement} img could also be a figure if blocks are enabled
32572             */
32573            imageupdate: true ,
32574            /**
32575             * @event imagedelete
32576             * Fires when on any editor when an image is deleted
32577             * @param {Roo.bootstrap.form.HtmlEditor} this
32578             * @param {HTMLElement} img could also be a figure if blocks are enabled
32579             */
32580            imagedelete: true  
32581     });
32582     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32583     if (!this.toolbars) {
32584         this.toolbars = [];
32585     }
32586     
32587     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32588     
32589 };
32590
32591
32592 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32593     
32594     
32595       /**
32596      * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32597      */
32598     toolbars : true,
32599     
32600      /**
32601     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32602     */
32603     btns : [],
32604    
32605      /**
32606      * @cfg {String} resize  (none|both|horizontal|vertical) - css resize of element
32607      */
32608     resize : false,
32609      /**
32610      * @cfg {Number} height (in pixels)
32611      */   
32612     height: 300,
32613    /**
32614      * @cfg {Number} width (in pixels)
32615      */   
32616     width: false,
32617     
32618     /**
32619      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32620      * 
32621      */
32622     stylesheets: false,
32623     
32624     // id of frame..
32625     frameId: false,
32626     
32627     // private properties
32628     validationEvent : false,
32629     deferHeight: true,
32630     initialized : false,
32631     activated : false,
32632     
32633     onFocus : Roo.emptyFn,
32634     iframePad:3,
32635     hideMode:'offsets',
32636     
32637     tbContainer : false,
32638     
32639     bodyCls : '',
32640     
32641     toolbarContainer :function() {
32642         return this.wrap.select('.x-html-editor-tb',true).first();
32643     },
32644
32645     /**
32646      * Protected method that will not generally be called directly. It
32647      * is called when the editor creates its toolbar. Override this method if you need to
32648      * add custom toolbar buttons.
32649      * @param {HtmlEditor} editor
32650      */
32651     createToolbar : function()
32652     {
32653         //Roo.log('renewing');
32654         //Roo.log("create toolbars");
32655         if (this.toolbars === false) {
32656             return;
32657         }
32658         if (this.toolbars === true) {
32659             this.toolbars = [ 'Standard' ];
32660         }
32661         
32662         var ar = Array.from(this.toolbars);
32663         this.toolbars = [];
32664         ar.forEach(function(t,i) {
32665             if (typeof(t) == 'string') {
32666                 t = {
32667                     xtype : t
32668                 };
32669             }
32670             if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
32671                 t.editor = this;
32672                 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
32673                 t = Roo.factory(t);
32674             }
32675             this.toolbars[i] = t;
32676             this.toolbars[i].render(this.toolbarContainer());
32677         }, this);
32678         
32679         
32680     },
32681
32682      
32683     // private
32684     onRender : function(ct, position)
32685     {
32686        // Roo.log("Call onRender: " + this.xtype);
32687         var _t = this;
32688         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32689       
32690         this.wrap = this.inputEl().wrap({
32691             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32692         });
32693         
32694         this.editorcore.onRender(ct, position);
32695          
32696          
32697         this.createToolbar(this);
32698        
32699         
32700           
32701         
32702     },
32703
32704     // private
32705     onResize : function(w, h)
32706     {
32707         Roo.log('resize: ' +w + ',' + h );
32708         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32709         var ew = false;
32710         var eh = false;
32711         
32712         if(this.inputEl() ){
32713             if(typeof w == 'number'){
32714                 var aw = w - this.wrap.getFrameWidth('lr');
32715                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32716                 ew = aw;
32717             }
32718             if(typeof h == 'number'){
32719                  var tbh = -11;  // fixme it needs to tool bar size!
32720                 for (var i =0; i < this.toolbars.length;i++) {
32721                     // fixme - ask toolbars for heights?
32722                     tbh += this.toolbars[i].el.getHeight();
32723                     //if (this.toolbars[i].footer) {
32724                     //    tbh += this.toolbars[i].footer.el.getHeight();
32725                     //}
32726                 }
32727               
32728                 
32729                 
32730                 
32731                 
32732                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32733                 ah -= 5; // knock a few pixes off for look..
32734                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32735                 var eh = ah;
32736             }
32737         }
32738         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32739         this.editorcore.onResize(ew,eh);
32740         
32741     },
32742
32743     /**
32744      * Toggles the editor between standard and source edit mode.
32745      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32746      */
32747     toggleSourceEdit : function(sourceEditMode)
32748     {
32749         this.editorcore.toggleSourceEdit(sourceEditMode);
32750         
32751         if(this.editorcore.sourceEditMode){
32752             Roo.log('editor - showing textarea');
32753             
32754 //            Roo.log('in');
32755 //            Roo.log(this.syncValue());
32756             this.syncValue();
32757             this.inputEl().removeClass(['hide', 'x-hidden']);
32758             this.inputEl().dom.removeAttribute('tabIndex');
32759             this.inputEl().focus();
32760         }else{
32761             Roo.log('editor - hiding textarea');
32762 //            Roo.log('out')
32763 //            Roo.log(this.pushValue()); 
32764             this.pushValue();
32765             
32766             this.inputEl().addClass(['hide', 'x-hidden']);
32767             this.inputEl().dom.setAttribute('tabIndex', -1);
32768             //this.deferFocus();
32769         }
32770          
32771         //if(this.resizable){
32772         //    this.setSize(this.wrap.getSize());
32773         //}
32774         
32775         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32776     },
32777  
32778     // private (for BoxComponent)
32779     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32780
32781     // private (for BoxComponent)
32782     getResizeEl : function(){
32783         return this.wrap;
32784     },
32785
32786     // private (for BoxComponent)
32787     getPositionEl : function(){
32788         return this.wrap;
32789     },
32790
32791     // private
32792     initEvents : function(){
32793         this.originalValue = this.getValue();
32794     },
32795
32796 //    /**
32797 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32798 //     * @method
32799 //     */
32800 //    markInvalid : Roo.emptyFn,
32801 //    /**
32802 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32803 //     * @method
32804 //     */
32805 //    clearInvalid : Roo.emptyFn,
32806
32807     setValue : function(v){
32808         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32809         this.editorcore.pushValue();
32810     },
32811
32812      
32813     // private
32814     deferFocus : function(){
32815         this.focus.defer(10, this);
32816     },
32817
32818     // doc'ed in Field
32819     focus : function(){
32820         this.editorcore.focus();
32821         
32822     },
32823       
32824
32825     // private
32826     onDestroy : function(){
32827         
32828         
32829         
32830         if(this.rendered){
32831             
32832             for (var i =0; i < this.toolbars.length;i++) {
32833                 // fixme - ask toolbars for heights?
32834                 this.toolbars[i].onDestroy();
32835             }
32836             
32837             this.wrap.dom.innerHTML = '';
32838             this.wrap.remove();
32839         }
32840     },
32841
32842     // private
32843     onFirstFocus : function(){
32844         //Roo.log("onFirstFocus");
32845         this.editorcore.onFirstFocus();
32846          for (var i =0; i < this.toolbars.length;i++) {
32847             this.toolbars[i].onFirstFocus();
32848         }
32849         
32850     },
32851     
32852     // private
32853     syncValue : function()
32854     {   
32855         this.editorcore.syncValue();
32856     },
32857     
32858     pushValue : function()
32859     {   
32860         this.editorcore.pushValue();
32861     }
32862      
32863     
32864     // hide stuff that is not compatible
32865     /**
32866      * @event blur
32867      * @hide
32868      */
32869     /**
32870      * @event change
32871      * @hide
32872      */
32873     /**
32874      * @event focus
32875      * @hide
32876      */
32877     /**
32878      * @event specialkey
32879      * @hide
32880      */
32881     /**
32882      * @cfg {String} fieldClass @hide
32883      */
32884     /**
32885      * @cfg {String} focusClass @hide
32886      */
32887     /**
32888      * @cfg {String} autoCreate @hide
32889      */
32890     /**
32891      * @cfg {String} inputType @hide
32892      */
32893      
32894     /**
32895      * @cfg {String} invalidText @hide
32896      */
32897     /**
32898      * @cfg {String} msgFx @hide
32899      */
32900     /**
32901      * @cfg {String} validateOnBlur @hide
32902      */
32903 });
32904  
32905     
32906    
32907    
32908    
32909       
32910 /**
32911  * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
32912  * @parent Roo.bootstrap.form.HtmlEditor
32913  * @extends Roo.bootstrap.nav.Simplebar
32914  * Basic Toolbar
32915  * 
32916  * @example
32917  * Usage:
32918  *
32919  new Roo.bootstrap.form.HtmlEditor({
32920     ....
32921     toolbars : [
32922         new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
32923             disable : { fonts: 1 , format: 1, ..., ... , ...],
32924             btns : [ .... ]
32925         })
32926     }
32927      
32928  * 
32929  * @cfg {Object} disable List of elements to disable..
32930  * @cfg {Array} btns List of additional buttons.
32931  * 
32932  * 
32933  * NEEDS Extra CSS? 
32934  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32935  */
32936  
32937 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
32938 {
32939     
32940     Roo.apply(this, config);
32941     
32942     // default disabled, based on 'good practice'..
32943     this.disable = this.disable || {};
32944     Roo.applyIf(this.disable, {
32945         fontSize : true,
32946         colors : true,
32947         specialElements : true
32948     });
32949     Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
32950     
32951     this.editor = config.editor;
32952     this.editorcore = config.editor.editorcore;
32953     
32954     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
32955     
32956     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32957     // dont call parent... till later.
32958 }
32959 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar,  {
32960      
32961     bar : true,
32962     
32963     editor : false,
32964     editorcore : false,
32965     
32966     
32967     formats : [
32968         "p" ,  
32969         "h1","h2","h3","h4","h5","h6", 
32970         "pre", "code", 
32971         "abbr", "acronym", "address", "cite", "samp", "var",
32972         'div','span'
32973     ],
32974     
32975     
32976     deleteBtn: false,
32977     
32978     onRender : function(ct, position)
32979     {
32980        // Roo.log("Call onRender: " + this.xtype);
32981         
32982        Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
32983        Roo.log(this.el);
32984        this.el.dom.style.marginBottom = '0';
32985        var _this = this;
32986        var editorcore = this.editorcore;
32987        var editor= this.editor;
32988        
32989        var children = [];
32990        var btn = function(id, cmd , toggle, handler, html){
32991        
32992             var  event = toggle ? 'toggle' : 'click';
32993        
32994             var a = {
32995                 size : 'sm',
32996                 xtype: 'Button',
32997                 xns: Roo.bootstrap,
32998                 //glyphicon : id,
32999                 btnid : id,
33000                 fa: id,
33001                 cls : 'roo-html-editor-btn-' + id,
33002                 cmd : cmd, // why id || cmd
33003                 enableToggle: toggle !== false,
33004                 html : html || '',
33005                 pressed : toggle ? false : null,
33006                 listeners : {}
33007             };
33008             a.listeners[toggle ? 'toggle' : 'click'] = function() {
33009                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
33010             };
33011             children.push(a);
33012             return a;
33013        }
33014        
33015     //    var cb_box = function...
33016         
33017         var style = {
33018                 xtype: 'Button',
33019                 size : 'sm',
33020                 xns: Roo.bootstrap,
33021                 fa : 'font',
33022                 cls : 'roo-html-editor-font-chooser',
33023                 //html : 'submit'
33024                 menu : {
33025                     xtype: 'Menu',
33026                     xns: Roo.bootstrap,
33027                     items:  []
33028                 }
33029         };
33030         Roo.each(this.formats, function(f) {
33031             style.menu.items.push({
33032                 xtype :'MenuItem',
33033                 xns: Roo.bootstrap,
33034                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33035                 tagname : f,
33036                 listeners : {
33037                     click : function()
33038                     {
33039                         editorcore.insertTag(this.tagname);
33040                         editor.focus();
33041                     }
33042                 }
33043                 
33044             });
33045         });
33046         children.push(style);   
33047         
33048         btn('bold',         'bold',true);
33049         btn('italic',       'italic',true);
33050         btn('underline',     'underline',true);
33051         btn('align-left',   'justifyleft',true);
33052         btn('align-center', 'justifycenter',true);
33053         btn('align-right' , 'justifyright',true);
33054         btn('link', false, true, this.onLinkClick);
33055         
33056         
33057         btn('image', false, true, this.onImageClick);
33058         btn('list','insertunorderedlist',true);
33059         btn('list-ol','insertorderedlist',true);
33060
33061         btn('pencil', false,true, function(btn){
33062                 Roo.log(this);
33063                 this.toggleSourceEdit(btn.pressed);
33064         });
33065         
33066         if (this.editor.btns.length > 0) {
33067             for (var i = 0; i<this.editor.btns.length; i++) {
33068                 children.push(this.editor.btns[i]);
33069             }
33070         }
33071         
33072         
33073          
33074         this.xtype = 'NavSimplebar'; // why?
33075         
33076         for(var i=0;i< children.length;i++) {
33077             
33078             this.buttons.add(this.addxtypeChild(children[i]));
33079             
33080         }
33081         this.buildToolbarDelete();
33082
33083         editor.on('editorevent', this.updateToolbar, this);
33084     },
33085     
33086     buildToolbarDelete : function()
33087     {
33088         
33089        /* this.addxtypeChild({
33090             xtype : 'Element',
33091             xns : Roo.bootstrap,
33092             cls : 'roo-htmleditor-fill'
33093         });
33094         */
33095         this.deleteBtn = this.addxtypeChild({
33096             size : 'sm',
33097             xtype: 'Button',
33098             xns: Roo.bootstrap,
33099             fa: 'trash',
33100             listeners : {
33101                 click : this.onDelete.createDelegate(this)
33102             }
33103         });
33104         this.deleteBtn.hide();     
33105         
33106     },
33107     
33108     onImageClick : function()
33109     {
33110         if (this.input) {
33111             this.input.un('change', this.onFileSelected, this);
33112         }
33113         this.input = Roo.get(document.body).createChild({ 
33114           tag: 'input', 
33115           type : 'file', 
33116           style : 'display:none', 
33117           multiple: 'multiple'
33118        });
33119         this.input.on('change', this.onFileSelected, this);
33120         this.input.dom.click();
33121     },
33122     
33123     onFileSelected : function(e)
33124     {
33125          e.preventDefault();
33126         
33127         if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33128             return;
33129         }
33130     
33131          
33132         this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33133     },
33134     
33135     addFiles : function(far, fire_add) {
33136
33137          
33138         var editor =  this.editorcore;
33139   
33140         if (!far.length) {
33141             if (fire_add) {
33142                 this.editor.syncValue();
33143                 editor.owner.fireEvent('editorevent', editor.owner, false);
33144                 editor.owner.fireEvent('imageadd', editor.owner, false);
33145             }
33146             return;
33147         }
33148         
33149         var f = far.pop();
33150         
33151         if (!f.type.match(/^image/)) {
33152             this.addFiles(far, fire_add);
33153             return;
33154         }
33155          
33156         var sn = this.selectedNode;
33157         
33158         var bl = sn  && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33159         
33160         
33161         var reader = new FileReader();
33162         reader.addEventListener('load', (function() {
33163             if (bl) {
33164                 bl.image_src = reader.result;
33165                 //bl.caption = f.name;
33166                 bl.updateElement(sn);
33167                 this.editor.syncValue();
33168                 editor.owner.fireEvent('editorevent', editor.owner, false);
33169                 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33170                 // we only do the first file!! and replace.
33171                 return;
33172             }
33173             if (this.editorcore.enableBlocks) {
33174                 var fig = new Roo.htmleditor.BlockFigure({
33175                     image_src :  reader.result,
33176                     caption : '',
33177                     caption_display : 'none'  //default to hide captions..
33178                  });
33179                 editor.insertAtCursor(fig.toHTML());
33180                 this.addFiles(far, true);
33181                 return;
33182             }
33183             // just a standard img..
33184             if (sn && sn.tagName.toUpperCase() == 'IMG') {
33185                 sn.src = reader.result;
33186                 this.editor.syncValue();
33187                 editor.owner.fireEvent('editorevent', editor.owner, false);
33188                 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33189                 return;
33190             }
33191             editor.insertAtCursor('<img src="' + reader.result +'">');
33192             this.addFiles(far, true);
33193             
33194         }).createDelegate(this));
33195         reader.readAsDataURL(f);
33196         
33197     
33198      },
33199     
33200     
33201     onBtnClick : function(id)
33202     {
33203        this.editorcore.relayCmd(id);
33204        this.editorcore.focus();
33205     },
33206     
33207     onLinkClick : function(btn) {
33208         var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33209                 this.selectedNode.getAttribute('href') : '';
33210             
33211         Roo.bootstrap.MessageBox.show({
33212             title : "Add / Edit Link URL",
33213             msg : "Enter the URL for the link",
33214             buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33215             minWidth: 250,
33216             scope : this,
33217             prompt:true,
33218             multiline: false,
33219             modal : true,
33220             value : url,
33221             fn:  function(pressed, newurl) {
33222                 if (pressed != 'ok') {
33223                     this.editorcore.focus();
33224                     return;
33225                 }
33226                 if (url != '') {
33227                     this.selectedNode.setAttribute('href', newurl);
33228                     return;
33229                 }
33230                 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33231                     this.editorcore.relayCmd('createlink', newurl);
33232                 }
33233                 this.editorcore.focus();
33234             }
33235         });
33236     },
33237     /**
33238      * Protected method that will not generally be called directly. It triggers
33239      * a toolbar update by reading the markup state of the current selection in the editor.
33240      */
33241     updateToolbar: function(editor ,ev, sel){
33242
33243         if(!this.editorcore.activated){
33244             this.editor.onFirstFocus(); // is this neeed?
33245             return;
33246         }
33247
33248         var btns = this.buttons; 
33249         var doc = this.editorcore.doc;
33250         var hasToggle  = false;
33251         btns.each(function(e) {
33252             if (e.enableToggle && e.cmd) {
33253                 hasToggle = hasToggle  || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33254                 e.setActive(doc.queryCommandState(e.cmd));
33255             }
33256         }, this);
33257         
33258         
33259         if (ev &&
33260             (ev.type == 'mouseup' || ev.type == 'click' ) &&
33261             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33262             // they have click on an image...
33263             // let's see if we can change the selection...
33264             sel = ev.target;
33265             
33266         }
33267         
33268         var ans = this.editorcore.getAllAncestors();
33269         if (!sel) { 
33270             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
33271             sel = sel ? sel : this.editorcore.doc.body;
33272             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33273             
33274         }
33275         
33276         var lastSel = this.selectedNode;
33277         this.selectedNode = sel;
33278          
33279         // ok see if we are editing a block?
33280         
33281         var db = false;
33282         // you are not actually selecting the block.
33283         if (sel && sel.hasAttribute('data-block')) {
33284             db = sel;
33285         } else if (sel && sel.closest('[data-block]')) {
33286             db = sel.closest('[data-block]');
33287         }
33288         
33289         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33290             e.classList.remove('roo-ed-selection');
33291         });
33292         
33293         var block = false;
33294         if (db && this.editorcore.enableBlocks) {
33295             block = Roo.htmleditor.Block.factory(db);
33296             
33297             if (block) {
33298                 db.className =  (db.classList.length > 0  ? db.className + ' ' : '') +
33299                     ' roo-ed-selection';
33300                 sel = this.selectedNode = db;
33301             }
33302         }
33303         
33304         // highlight the 'a'..
33305         var tn = sel && sel.tagName.toUpperCase() || '';
33306         if (!block && sel && tn != 'A') {
33307             var asel = sel.closest('A');
33308             if (asel) {
33309                 sel = asel;
33310             }
33311         }
33312        
33313         btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33314         btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33315         btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33316         
33317         Roo.bootstrap.menu.Manager.hideAll();
33318          
33319         
33320         
33321         
33322         
33323         // handle delete button..
33324         if (hasToggle || (tn.length && tn == 'BODY')) {
33325             this.deleteBtn.hide();
33326             return;
33327             
33328         }
33329         this.deleteBtn.show();
33330         
33331         
33332         
33333         //this.editorsyncValue();
33334     },
33335     onFirstFocus: function() {
33336         this.buttons.each(function(item){
33337            item.enable();
33338         });
33339     },
33340     
33341     onDelete : function()
33342     {
33343         var range = this.editorcore.createRange();
33344         var selection = this.editorcore.getSelection();
33345         var sn = this.selectedNode;
33346         range.setStart(sn,0);
33347         range.setEnd(sn,0); 
33348         
33349         
33350         if (sn.hasAttribute('data-block')) {
33351             var block = Roo.htmleditor.Block.factory(this.selectedNode);
33352             if (block) {
33353                 sn = block.removeNode();
33354                 sn.parentNode.removeChild(sn);
33355                 selection.removeAllRanges();
33356                 selection.addRange(range);
33357                 this.updateToolbar(null, null, null);
33358                 if (sn.tagName.toUpperCase() == 'FIGURE') {
33359                     this.editor.syncValue();
33360                     this.editor.fireEvent('imagedelete', this.editor, sn);
33361                 }
33362                 
33363                 this.selectedNode = false;
33364                 this.editorcore.fireEditorEvent(false);
33365                 return;
33366             }   
33367              
33368         }
33369         if (!sn) {
33370             return; // should not really happen..
33371         }
33372         if (sn && sn.tagName == 'BODY') {
33373             return;
33374         }
33375         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33376         
33377         // remove and keep parents.
33378         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33379         a.replaceTag(sn);
33380         
33381         selection.removeAllRanges();
33382         selection.addRange(range);
33383         if (sn.tagName.toUpperCase() == 'IMG"') {
33384             this.editor.syncValue();
33385             this.editor.fireEvent('imagedelete', this.editor, sn);
33386         }
33387         
33388         this.selectedNode = false;
33389         this.editorcore.fireEditorEvent(false);
33390         
33391         
33392     },
33393     
33394     
33395     toggleSourceEdit : function(sourceEditMode){
33396         
33397           
33398         if(sourceEditMode){
33399             Roo.log("disabling buttons");
33400            this.buttons.each( function(item){
33401                 if(item.cmd != 'pencil'){
33402                     item.disable();
33403                 }
33404             });
33405           
33406         }else{
33407             Roo.log("enabling buttons");
33408             if(this.editorcore.initialized){
33409                 this.buttons.each( function(item){
33410                     item.enable();
33411                 });
33412             }
33413             
33414         }
33415         Roo.log("calling toggole on editor");
33416         // tell the editor that it's been pressed..
33417         this.editor.toggleSourceEdit(sourceEditMode);
33418        
33419     }
33420 });
33421
33422
33423
33424
33425  
33426 /*
33427  * - LGPL
33428  */
33429
33430 /**
33431  * @class Roo.bootstrap.form.Markdown
33432  * @extends Roo.bootstrap.form.TextArea
33433  * Bootstrap Showdown editable area
33434  * @cfg {string} content
33435  * 
33436  * @constructor
33437  * Create a new Showdown
33438  */
33439
33440 Roo.bootstrap.form.Markdown = function(config){
33441     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33442    
33443 };
33444
33445 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33446     
33447     editing :false,
33448     
33449     initEvents : function()
33450     {
33451         
33452         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33453         this.markdownEl = this.el.createChild({
33454             cls : 'roo-markdown-area'
33455         });
33456         this.inputEl().addClass('d-none');
33457         if (this.getValue() == '') {
33458             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33459             
33460         } else {
33461             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33462         }
33463         this.markdownEl.on('click', this.toggleTextEdit, this);
33464         this.on('blur', this.toggleTextEdit, this);
33465         this.on('specialkey', this.resizeTextArea, this);
33466     },
33467     
33468     toggleTextEdit : function()
33469     {
33470         var sh = this.markdownEl.getHeight();
33471         this.inputEl().addClass('d-none');
33472         this.markdownEl.addClass('d-none');
33473         if (!this.editing) {
33474             // show editor?
33475             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33476             this.inputEl().removeClass('d-none');
33477             this.inputEl().focus();
33478             this.editing = true;
33479             return;
33480         }
33481         // show showdown...
33482         this.updateMarkdown();
33483         this.markdownEl.removeClass('d-none');
33484         this.editing = false;
33485         return;
33486     },
33487     updateMarkdown : function()
33488     {
33489         if (this.getValue() == '') {
33490             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33491             return;
33492         }
33493  
33494         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33495     },
33496     
33497     resizeTextArea: function () {
33498         
33499         var sh = 100;
33500         Roo.log([sh, this.getValue().split("\n").length * 30]);
33501         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33502     },
33503     setValue : function(val)
33504     {
33505         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33506         if (!this.editing) {
33507             this.updateMarkdown();
33508         }
33509         
33510     },
33511     focus : function()
33512     {
33513         if (!this.editing) {
33514             this.toggleTextEdit();
33515         }
33516         
33517     }
33518
33519
33520 });/*
33521  * Based on:
33522  * Ext JS Library 1.1.1
33523  * Copyright(c) 2006-2007, Ext JS, LLC.
33524  *
33525  * Originally Released Under LGPL - original licence link has changed is not relivant.
33526  *
33527  * Fork - LGPL
33528  * <script type="text/javascript">
33529  */
33530  
33531 /**
33532  * @class Roo.bootstrap.PagingToolbar
33533  * @extends Roo.bootstrap.nav.Simplebar
33534  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33535  * @constructor
33536  * Create a new PagingToolbar
33537  * @param {Object} config The config object
33538  * @param {Roo.data.Store} store
33539  */
33540 Roo.bootstrap.PagingToolbar = function(config)
33541 {
33542     // old args format still supported... - xtype is prefered..
33543         // created from xtype...
33544     
33545     this.ds = config.dataSource;
33546     
33547     if (config.store && !this.ds) {
33548         this.store= Roo.factory(config.store, Roo.data);
33549         this.ds = this.store;
33550         this.ds.xmodule = this.xmodule || false;
33551     }
33552     
33553     this.toolbarItems = [];
33554     if (config.items) {
33555         this.toolbarItems = config.items;
33556     }
33557     
33558     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33559     
33560     this.cursor = 0;
33561     
33562     if (this.ds) { 
33563         this.bind(this.ds);
33564     }
33565     
33566     if (Roo.bootstrap.version == 4) {
33567         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33568     } else {
33569         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33570     }
33571     
33572 };
33573
33574 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33575     /**
33576      * @cfg {Roo.bootstrap.Button} buttons[]
33577      * Buttons for the toolbar
33578      */
33579      /**
33580      * @cfg {Roo.data.Store} store
33581      * The underlying data store providing the paged data
33582      */
33583     /**
33584      * @cfg {String/HTMLElement/Element} container
33585      * container The id or element that will contain the toolbar
33586      */
33587     /**
33588      * @cfg {Boolean} displayInfo
33589      * True to display the displayMsg (defaults to false)
33590      */
33591     /**
33592      * @cfg {Number} pageSize
33593      * The number of records to display per page (defaults to 20)
33594      */
33595     pageSize: 20,
33596     /**
33597      * @cfg {String} displayMsg
33598      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33599      */
33600     displayMsg : 'Displaying {0} - {1} of {2}',
33601     /**
33602      * @cfg {String} emptyMsg
33603      * The message to display when no records are found (defaults to "No data to display")
33604      */
33605     emptyMsg : 'No data to display',
33606     /**
33607      * Customizable piece of the default paging text (defaults to "Page")
33608      * @type String
33609      */
33610     beforePageText : "Page",
33611     /**
33612      * Customizable piece of the default paging text (defaults to "of %0")
33613      * @type String
33614      */
33615     afterPageText : "of {0}",
33616     /**
33617      * Customizable piece of the default paging text (defaults to "First Page")
33618      * @type String
33619      */
33620     firstText : "First Page",
33621     /**
33622      * Customizable piece of the default paging text (defaults to "Previous Page")
33623      * @type String
33624      */
33625     prevText : "Previous Page",
33626     /**
33627      * Customizable piece of the default paging text (defaults to "Next Page")
33628      * @type String
33629      */
33630     nextText : "Next Page",
33631     /**
33632      * Customizable piece of the default paging text (defaults to "Last Page")
33633      * @type String
33634      */
33635     lastText : "Last Page",
33636     /**
33637      * Customizable piece of the default paging text (defaults to "Refresh")
33638      * @type String
33639      */
33640     refreshText : "Refresh",
33641
33642     buttons : false,
33643     // private
33644     onRender : function(ct, position) 
33645     {
33646         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33647         this.navgroup.parentId = this.id;
33648         this.navgroup.onRender(this.el, null);
33649         // add the buttons to the navgroup
33650         
33651         if(this.displayInfo){
33652             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33653             this.displayEl = this.el.select('.x-paging-info', true).first();
33654 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33655 //            this.displayEl = navel.el.select('span',true).first();
33656         }
33657         
33658         var _this = this;
33659         
33660         if(this.buttons){
33661             Roo.each(_this.buttons, function(e){ // this might need to use render????
33662                Roo.factory(e).render(_this.el);
33663             });
33664         }
33665             
33666         Roo.each(_this.toolbarItems, function(e) {
33667             _this.navgroup.addItem(e);
33668         });
33669         
33670         
33671         this.first = this.navgroup.addItem({
33672             tooltip: this.firstText,
33673             cls: "prev btn-outline-secondary",
33674             html : ' <i class="fa fa-step-backward"></i>',
33675             disabled: true,
33676             preventDefault: true,
33677             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33678         });
33679         
33680         this.prev =  this.navgroup.addItem({
33681             tooltip: this.prevText,
33682             cls: "prev btn-outline-secondary",
33683             html : ' <i class="fa fa-backward"></i>',
33684             disabled: true,
33685             preventDefault: true,
33686             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
33687         });
33688     //this.addSeparator();
33689         
33690         
33691         var field = this.navgroup.addItem( {
33692             tagtype : 'span',
33693             cls : 'x-paging-position  btn-outline-secondary',
33694              disabled: true,
33695             html : this.beforePageText  +
33696                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33697                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
33698          } ); //?? escaped?
33699         
33700         this.field = field.el.select('input', true).first();
33701         this.field.on("keydown", this.onPagingKeydown, this);
33702         this.field.on("focus", function(){this.dom.select();});
33703     
33704     
33705         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
33706         //this.field.setHeight(18);
33707         //this.addSeparator();
33708         this.next = this.navgroup.addItem({
33709             tooltip: this.nextText,
33710             cls: "next btn-outline-secondary",
33711             html : ' <i class="fa fa-forward"></i>',
33712             disabled: true,
33713             preventDefault: true,
33714             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
33715         });
33716         this.last = this.navgroup.addItem({
33717             tooltip: this.lastText,
33718             html : ' <i class="fa fa-step-forward"></i>',
33719             cls: "next btn-outline-secondary",
33720             disabled: true,
33721             preventDefault: true,
33722             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
33723         });
33724     //this.addSeparator();
33725         this.loading = this.navgroup.addItem({
33726             tooltip: this.refreshText,
33727             cls: "btn-outline-secondary",
33728             html : ' <i class="fa fa-refresh"></i>',
33729             preventDefault: true,
33730             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33731         });
33732         
33733     },
33734
33735     // private
33736     updateInfo : function(){
33737         if(this.displayEl){
33738             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33739             var msg = count == 0 ?
33740                 this.emptyMsg :
33741                 String.format(
33742                     this.displayMsg,
33743                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
33744                 );
33745             this.displayEl.update(msg);
33746         }
33747     },
33748
33749     // private
33750     onLoad : function(ds, r, o)
33751     {
33752         this.cursor = o.params && o.params.start ? o.params.start : 0;
33753         
33754         var d = this.getPageData(),
33755             ap = d.activePage,
33756             ps = d.pages;
33757         
33758         
33759         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33760         this.field.dom.value = ap;
33761         this.first.setDisabled(ap == 1);
33762         this.prev.setDisabled(ap == 1);
33763         this.next.setDisabled(ap == ps);
33764         this.last.setDisabled(ap == ps);
33765         this.loading.enable();
33766         this.updateInfo();
33767     },
33768
33769     // private
33770     getPageData : function(){
33771         var total = this.ds.getTotalCount();
33772         return {
33773             total : total,
33774             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33775             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33776         };
33777     },
33778
33779     // private
33780     onLoadError : function(proxy, o){
33781         this.loading.enable();
33782         if (this.ds.events.loadexception.listeners.length  < 2) {
33783             // nothing has been assigned to loadexception except this...
33784             // so 
33785             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33786
33787         }
33788     },
33789
33790     // private
33791     onPagingKeydown : function(e){
33792         var k = e.getKey();
33793         var d = this.getPageData();
33794         if(k == e.RETURN){
33795             var v = this.field.dom.value, pageNum;
33796             if(!v || isNaN(pageNum = parseInt(v, 10))){
33797                 this.field.dom.value = d.activePage;
33798                 return;
33799             }
33800             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33801             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33802             e.stopEvent();
33803         }
33804         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))
33805         {
33806           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33807           this.field.dom.value = pageNum;
33808           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33809           e.stopEvent();
33810         }
33811         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33812         {
33813           var v = this.field.dom.value, pageNum; 
33814           var increment = (e.shiftKey) ? 10 : 1;
33815           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33816                 increment *= -1;
33817           }
33818           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33819             this.field.dom.value = d.activePage;
33820             return;
33821           }
33822           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33823           {
33824             this.field.dom.value = parseInt(v, 10) + increment;
33825             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33826             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33827           }
33828           e.stopEvent();
33829         }
33830     },
33831
33832     // private
33833     beforeLoad : function(){
33834         if(this.loading){
33835             this.loading.disable();
33836         }
33837     },
33838
33839     // private
33840     onClick : function(which){
33841         
33842         var ds = this.ds;
33843         if (!ds) {
33844             return;
33845         }
33846         
33847         switch(which){
33848             case "first":
33849                 ds.load({params:{start: 0, limit: this.pageSize}});
33850             break;
33851             case "prev":
33852                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33853             break;
33854             case "next":
33855                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33856             break;
33857             case "last":
33858                 var total = ds.getTotalCount();
33859                 var extra = total % this.pageSize;
33860                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33861                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33862             break;
33863             case "refresh":
33864                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33865             break;
33866         }
33867     },
33868
33869     /**
33870      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33871      * @param {Roo.data.Store} store The data store to unbind
33872      */
33873     unbind : function(ds){
33874         ds.un("beforeload", this.beforeLoad, this);
33875         ds.un("load", this.onLoad, this);
33876         ds.un("loadexception", this.onLoadError, this);
33877         ds.un("remove", this.updateInfo, this);
33878         ds.un("add", this.updateInfo, this);
33879         this.ds = undefined;
33880     },
33881
33882     /**
33883      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33884      * @param {Roo.data.Store} store The data store to bind
33885      */
33886     bind : function(ds){
33887         ds.on("beforeload", this.beforeLoad, this);
33888         ds.on("load", this.onLoad, this);
33889         ds.on("loadexception", this.onLoadError, this);
33890         ds.on("remove", this.updateInfo, this);
33891         ds.on("add", this.updateInfo, this);
33892         this.ds = ds;
33893     }
33894 });/*
33895  * - LGPL
33896  *
33897  * element
33898  * 
33899  */
33900
33901 /**
33902  * @class Roo.bootstrap.MessageBar
33903  * @extends Roo.bootstrap.Component
33904  * Bootstrap MessageBar class
33905  * @cfg {String} html contents of the MessageBar
33906  * @cfg {String} weight (info | success | warning | danger) default info
33907  * @cfg {String} beforeClass insert the bar before the given class
33908  * @cfg {Boolean} closable (true | false) default false
33909  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33910  * 
33911  * @constructor
33912  * Create a new Element
33913  * @param {Object} config The config object
33914  */
33915
33916 Roo.bootstrap.MessageBar = function(config){
33917     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33918 };
33919
33920 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33921     
33922     html: '',
33923     weight: 'info',
33924     closable: false,
33925     fixed: false,
33926     beforeClass: 'bootstrap-sticky-wrap',
33927     
33928     getAutoCreate : function(){
33929         
33930         var cfg = {
33931             tag: 'div',
33932             cls: 'alert alert-dismissable alert-' + this.weight,
33933             cn: [
33934                 {
33935                     tag: 'span',
33936                     cls: 'message',
33937                     html: this.html || ''
33938                 }
33939             ]
33940         };
33941         
33942         if(this.fixed){
33943             cfg.cls += ' alert-messages-fixed';
33944         }
33945         
33946         if(this.closable){
33947             cfg.cn.push({
33948                 tag: 'button',
33949                 cls: 'close',
33950                 html: 'x'
33951             });
33952         }
33953         
33954         return cfg;
33955     },
33956     
33957     onRender : function(ct, position)
33958     {
33959         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33960         
33961         if(!this.el){
33962             var cfg = Roo.apply({},  this.getAutoCreate());
33963             cfg.id = Roo.id();
33964             
33965             if (this.cls) {
33966                 cfg.cls += ' ' + this.cls;
33967             }
33968             if (this.style) {
33969                 cfg.style = this.style;
33970             }
33971             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33972             
33973             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33974         }
33975         
33976         this.el.select('>button.close').on('click', this.hide, this);
33977         
33978     },
33979     
33980     show : function()
33981     {
33982         if (!this.rendered) {
33983             this.render();
33984         }
33985         
33986         this.el.show();
33987         
33988         this.fireEvent('show', this);
33989         
33990     },
33991     
33992     hide : function()
33993     {
33994         if (!this.rendered) {
33995             this.render();
33996         }
33997         
33998         this.el.hide();
33999         
34000         this.fireEvent('hide', this);
34001     },
34002     
34003     update : function()
34004     {
34005 //        var e = this.el.dom.firstChild;
34006 //        
34007 //        if(this.closable){
34008 //            e = e.nextSibling;
34009 //        }
34010 //        
34011 //        e.data = this.html || '';
34012
34013         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34014     }
34015    
34016 });
34017
34018  
34019
34020      /*
34021  * - LGPL
34022  *
34023  * Graph
34024  * 
34025  */
34026
34027
34028 /**
34029  * @class Roo.bootstrap.Graph
34030  * @extends Roo.bootstrap.Component
34031  * Bootstrap Graph class
34032 > Prameters
34033  -sm {number} sm 4
34034  -md {number} md 5
34035  @cfg {String} graphtype  bar | vbar | pie
34036  @cfg {number} g_x coodinator | centre x (pie)
34037  @cfg {number} g_y coodinator | centre y (pie)
34038  @cfg {number} g_r radius (pie)
34039  @cfg {number} g_height height of the chart (respected by all elements in the set)
34040  @cfg {number} g_width width of the chart (respected by all elements in the set)
34041  @cfg {Object} title The title of the chart
34042     
34043  -{Array}  values
34044  -opts (object) options for the chart 
34045      o {
34046      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34047      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34048      o vgutter (number)
34049      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.
34050      o stacked (boolean) whether or not to tread values as in a stacked bar chart
34051      o to
34052      o stretch (boolean)
34053      o }
34054  -opts (object) options for the pie
34055      o{
34056      o cut
34057      o startAngle (number)
34058      o endAngle (number)
34059      } 
34060  *
34061  * @constructor
34062  * Create a new Input
34063  * @param {Object} config The config object
34064  */
34065
34066 Roo.bootstrap.Graph = function(config){
34067     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34068     
34069     this.addEvents({
34070         // img events
34071         /**
34072          * @event click
34073          * The img click event for the img.
34074          * @param {Roo.EventObject} e
34075          */
34076         "click" : true
34077     });
34078 };
34079
34080 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
34081     
34082     sm: 4,
34083     md: 5,
34084     graphtype: 'bar',
34085     g_height: 250,
34086     g_width: 400,
34087     g_x: 50,
34088     g_y: 50,
34089     g_r: 30,
34090     opts:{
34091         //g_colors: this.colors,
34092         g_type: 'soft',
34093         g_gutter: '20%'
34094
34095     },
34096     title : false,
34097
34098     getAutoCreate : function(){
34099         
34100         var cfg = {
34101             tag: 'div',
34102             html : null
34103         };
34104         
34105         
34106         return  cfg;
34107     },
34108
34109     onRender : function(ct,position){
34110         
34111         
34112         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34113         
34114         if (typeof(Raphael) == 'undefined') {
34115             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34116             return;
34117         }
34118         
34119         this.raphael = Raphael(this.el.dom);
34120         
34121                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34122                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34123                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34124                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34125                 /*
34126                 r.text(160, 10, "Single Series Chart").attr(txtattr);
34127                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34128                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34129                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34130                 
34131                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34132                 r.barchart(330, 10, 300, 220, data1);
34133                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34134                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34135                 */
34136                 
34137                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34138                 // r.barchart(30, 30, 560, 250,  xdata, {
34139                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34140                 //     axis : "0 0 1 1",
34141                 //     axisxlabels :  xdata
34142                 //     //yvalues : cols,
34143                    
34144                 // });
34145 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34146 //        
34147 //        this.load(null,xdata,{
34148 //                axis : "0 0 1 1",
34149 //                axisxlabels :  xdata
34150 //                });
34151
34152     },
34153
34154     load : function(graphtype,xdata,opts)
34155     {
34156         this.raphael.clear();
34157         if(!graphtype) {
34158             graphtype = this.graphtype;
34159         }
34160         if(!opts){
34161             opts = this.opts;
34162         }
34163         var r = this.raphael,
34164             fin = function () {
34165                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34166             },
34167             fout = function () {
34168                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34169             },
34170             pfin = function() {
34171                 this.sector.stop();
34172                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34173
34174                 if (this.label) {
34175                     this.label[0].stop();
34176                     this.label[0].attr({ r: 7.5 });
34177                     this.label[1].attr({ "font-weight": 800 });
34178                 }
34179             },
34180             pfout = function() {
34181                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34182
34183                 if (this.label) {
34184                     this.label[0].animate({ r: 5 }, 500, "bounce");
34185                     this.label[1].attr({ "font-weight": 400 });
34186                 }
34187             };
34188
34189         switch(graphtype){
34190             case 'bar':
34191                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34192                 break;
34193             case 'hbar':
34194                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34195                 break;
34196             case 'pie':
34197 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
34198 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34199 //            
34200                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34201                 
34202                 break;
34203
34204         }
34205         
34206         if(this.title){
34207             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34208         }
34209         
34210     },
34211     
34212     setTitle: function(o)
34213     {
34214         this.title = o;
34215     },
34216     
34217     initEvents: function() {
34218         
34219         if(!this.href){
34220             this.el.on('click', this.onClick, this);
34221         }
34222     },
34223     
34224     onClick : function(e)
34225     {
34226         Roo.log('img onclick');
34227         this.fireEvent('click', this, e);
34228     }
34229    
34230 });
34231
34232  
34233 Roo.bootstrap.dash = {};/*
34234  * - LGPL
34235  *
34236  * numberBox
34237  * 
34238  */
34239 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34240
34241 /**
34242  * @class Roo.bootstrap.dash.NumberBox
34243  * @extends Roo.bootstrap.Component
34244  * Bootstrap NumberBox class
34245  * @cfg {String} headline Box headline
34246  * @cfg {String} content Box content
34247  * @cfg {String} icon Box icon
34248  * @cfg {String} footer Footer text
34249  * @cfg {String} fhref Footer href
34250  * 
34251  * @constructor
34252  * Create a new NumberBox
34253  * @param {Object} config The config object
34254  */
34255
34256
34257 Roo.bootstrap.dash.NumberBox = function(config){
34258     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34259     
34260 };
34261
34262 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
34263     
34264     headline : '',
34265     content : '',
34266     icon : '',
34267     footer : '',
34268     fhref : '',
34269     ficon : '',
34270     
34271     getAutoCreate : function(){
34272         
34273         var cfg = {
34274             tag : 'div',
34275             cls : 'small-box ',
34276             cn : [
34277                 {
34278                     tag : 'div',
34279                     cls : 'inner',
34280                     cn :[
34281                         {
34282                             tag : 'h3',
34283                             cls : 'roo-headline',
34284                             html : this.headline
34285                         },
34286                         {
34287                             tag : 'p',
34288                             cls : 'roo-content',
34289                             html : this.content
34290                         }
34291                     ]
34292                 }
34293             ]
34294         };
34295         
34296         if(this.icon){
34297             cfg.cn.push({
34298                 tag : 'div',
34299                 cls : 'icon',
34300                 cn :[
34301                     {
34302                         tag : 'i',
34303                         cls : 'ion ' + this.icon
34304                     }
34305                 ]
34306             });
34307         }
34308         
34309         if(this.footer){
34310             var footer = {
34311                 tag : 'a',
34312                 cls : 'small-box-footer',
34313                 href : this.fhref || '#',
34314                 html : this.footer
34315             };
34316             
34317             cfg.cn.push(footer);
34318             
34319         }
34320         
34321         return  cfg;
34322     },
34323
34324     onRender : function(ct,position){
34325         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34326
34327
34328        
34329                 
34330     },
34331
34332     setHeadline: function (value)
34333     {
34334         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34335     },
34336     
34337     setFooter: function (value, href)
34338     {
34339         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34340         
34341         if(href){
34342             this.el.select('a.small-box-footer',true).first().attr('href', href);
34343         }
34344         
34345     },
34346
34347     setContent: function (value)
34348     {
34349         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34350     },
34351
34352     initEvents: function() 
34353     {   
34354         
34355     }
34356     
34357 });
34358
34359  
34360 /*
34361  * - LGPL
34362  *
34363  * TabBox
34364  * 
34365  */
34366 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34367
34368 /**
34369  * @class Roo.bootstrap.dash.TabBox
34370  * @extends Roo.bootstrap.Component
34371  * @children Roo.bootstrap.dash.TabPane
34372  * Bootstrap TabBox class
34373  * @cfg {String} title Title of the TabBox
34374  * @cfg {String} icon Icon of the TabBox
34375  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34376  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34377  * 
34378  * @constructor
34379  * Create a new TabBox
34380  * @param {Object} config The config object
34381  */
34382
34383
34384 Roo.bootstrap.dash.TabBox = function(config){
34385     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34386     this.addEvents({
34387         // raw events
34388         /**
34389          * @event addpane
34390          * When a pane is added
34391          * @param {Roo.bootstrap.dash.TabPane} pane
34392          */
34393         "addpane" : true,
34394         /**
34395          * @event activatepane
34396          * When a pane is activated
34397          * @param {Roo.bootstrap.dash.TabPane} pane
34398          */
34399         "activatepane" : true
34400         
34401          
34402     });
34403     
34404     this.panes = [];
34405 };
34406
34407 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34408
34409     title : '',
34410     icon : false,
34411     showtabs : true,
34412     tabScrollable : false,
34413     
34414     getChildContainer : function()
34415     {
34416         return this.el.select('.tab-content', true).first();
34417     },
34418     
34419     getAutoCreate : function(){
34420         
34421         var header = {
34422             tag: 'li',
34423             cls: 'pull-left header',
34424             html: this.title,
34425             cn : []
34426         };
34427         
34428         if(this.icon){
34429             header.cn.push({
34430                 tag: 'i',
34431                 cls: 'fa ' + this.icon
34432             });
34433         }
34434         
34435         var h = {
34436             tag: 'ul',
34437             cls: 'nav nav-tabs pull-right',
34438             cn: [
34439                 header
34440             ]
34441         };
34442         
34443         if(this.tabScrollable){
34444             h = {
34445                 tag: 'div',
34446                 cls: 'tab-header',
34447                 cn: [
34448                     {
34449                         tag: 'ul',
34450                         cls: 'nav nav-tabs pull-right',
34451                         cn: [
34452                             header
34453                         ]
34454                     }
34455                 ]
34456             };
34457         }
34458         
34459         var cfg = {
34460             tag: 'div',
34461             cls: 'nav-tabs-custom',
34462             cn: [
34463                 h,
34464                 {
34465                     tag: 'div',
34466                     cls: 'tab-content no-padding',
34467                     cn: []
34468                 }
34469             ]
34470         };
34471
34472         return  cfg;
34473     },
34474     initEvents : function()
34475     {
34476         //Roo.log('add add pane handler');
34477         this.on('addpane', this.onAddPane, this);
34478     },
34479      /**
34480      * Updates the box title
34481      * @param {String} html to set the title to.
34482      */
34483     setTitle : function(value)
34484     {
34485         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34486     },
34487     onAddPane : function(pane)
34488     {
34489         this.panes.push(pane);
34490         //Roo.log('addpane');
34491         //Roo.log(pane);
34492         // tabs are rendere left to right..
34493         if(!this.showtabs){
34494             return;
34495         }
34496         
34497         var ctr = this.el.select('.nav-tabs', true).first();
34498          
34499          
34500         var existing = ctr.select('.nav-tab',true);
34501         var qty = existing.getCount();;
34502         
34503         
34504         var tab = ctr.createChild({
34505             tag : 'li',
34506             cls : 'nav-tab' + (qty ? '' : ' active'),
34507             cn : [
34508                 {
34509                     tag : 'a',
34510                     href:'#',
34511                     html : pane.title
34512                 }
34513             ]
34514         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34515         pane.tab = tab;
34516         
34517         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34518         if (!qty) {
34519             pane.el.addClass('active');
34520         }
34521         
34522                 
34523     },
34524     onTabClick : function(ev,un,ob,pane)
34525     {
34526         //Roo.log('tab - prev default');
34527         ev.preventDefault();
34528         
34529         
34530         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34531         pane.tab.addClass('active');
34532         //Roo.log(pane.title);
34533         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34534         // technically we should have a deactivate event.. but maybe add later.
34535         // and it should not de-activate the selected tab...
34536         this.fireEvent('activatepane', pane);
34537         pane.el.addClass('active');
34538         pane.fireEvent('activate');
34539         
34540         
34541     },
34542     
34543     getActivePane : function()
34544     {
34545         var r = false;
34546         Roo.each(this.panes, function(p) {
34547             if(p.el.hasClass('active')){
34548                 r = p;
34549                 return false;
34550             }
34551             
34552             return;
34553         });
34554         
34555         return r;
34556     }
34557     
34558     
34559 });
34560
34561  
34562 /*
34563  * - LGPL
34564  *
34565  * Tab pane
34566  * 
34567  */
34568 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34569 /**
34570  * @class Roo.bootstrap.TabPane
34571  * @extends Roo.bootstrap.Component
34572  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34573  * Bootstrap TabPane class
34574  * @cfg {Boolean} active (false | true) Default false
34575  * @cfg {String} title title of panel
34576
34577  * 
34578  * @constructor
34579  * Create a new TabPane
34580  * @param {Object} config The config object
34581  */
34582
34583 Roo.bootstrap.dash.TabPane = function(config){
34584     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34585     
34586     this.addEvents({
34587         // raw events
34588         /**
34589          * @event activate
34590          * When a pane is activated
34591          * @param {Roo.bootstrap.dash.TabPane} pane
34592          */
34593         "activate" : true
34594          
34595     });
34596 };
34597
34598 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34599     
34600     active : false,
34601     title : '',
34602     
34603     // the tabBox that this is attached to.
34604     tab : false,
34605      
34606     getAutoCreate : function() 
34607     {
34608         var cfg = {
34609             tag: 'div',
34610             cls: 'tab-pane'
34611         };
34612         
34613         if(this.active){
34614             cfg.cls += ' active';
34615         }
34616         
34617         return cfg;
34618     },
34619     initEvents  : function()
34620     {
34621         //Roo.log('trigger add pane handler');
34622         this.parent().fireEvent('addpane', this)
34623     },
34624     
34625      /**
34626      * Updates the tab title 
34627      * @param {String} html to set the title to.
34628      */
34629     setTitle: function(str)
34630     {
34631         if (!this.tab) {
34632             return;
34633         }
34634         this.title = str;
34635         this.tab.select('a', true).first().dom.innerHTML = str;
34636         
34637     }
34638     
34639     
34640     
34641 });
34642
34643  
34644
34645
34646  /*
34647  * - LGPL
34648  *
34649  * Tooltip
34650  * 
34651  */
34652
34653 /**
34654  * @class Roo.bootstrap.Tooltip
34655  * Bootstrap Tooltip class
34656  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34657  * to determine which dom element triggers the tooltip.
34658  * 
34659  * It needs to add support for additional attributes like tooltip-position
34660  * 
34661  * @constructor
34662  * Create a new Toolti
34663  * @param {Object} config The config object
34664  */
34665
34666 Roo.bootstrap.Tooltip = function(config){
34667     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34668     
34669     this.alignment = Roo.bootstrap.Tooltip.alignment;
34670     
34671     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34672         this.alignment = config.alignment;
34673     }
34674     
34675 };
34676
34677 Roo.apply(Roo.bootstrap.Tooltip, {
34678     /**
34679      * @function init initialize tooltip monitoring.
34680      * @static
34681      */
34682     currentEl : false,
34683     currentTip : false,
34684     currentRegion : false,
34685     
34686     //  init : delay?
34687     
34688     init : function()
34689     {
34690         Roo.get(document).on('mouseover', this.enter ,this);
34691         Roo.get(document).on('mouseout', this.leave, this);
34692          
34693         
34694         this.currentTip = new Roo.bootstrap.Tooltip();
34695     },
34696     
34697     enter : function(ev)
34698     {
34699         var dom = ev.getTarget();
34700         
34701         //Roo.log(['enter',dom]);
34702         var el = Roo.fly(dom);
34703         if (this.currentEl) {
34704             //Roo.log(dom);
34705             //Roo.log(this.currentEl);
34706             //Roo.log(this.currentEl.contains(dom));
34707             if (this.currentEl == el) {
34708                 return;
34709             }
34710             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34711                 return;
34712             }
34713
34714         }
34715         
34716         if (this.currentTip.el) {
34717             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34718         }    
34719         //Roo.log(ev);
34720         
34721         if(!el || el.dom == document){
34722             return;
34723         }
34724         
34725         var bindEl = el; 
34726         var pel = false;
34727         if (!el.attr('tooltip')) {
34728             pel = el.findParent("[tooltip]");
34729             if (pel) {
34730                 bindEl = Roo.get(pel);
34731             }
34732         }
34733         
34734        
34735         
34736         // you can not look for children, as if el is the body.. then everythign is the child..
34737         if (!pel && !el.attr('tooltip')) { //
34738             if (!el.select("[tooltip]").elements.length) {
34739                 return;
34740             }
34741             // is the mouse over this child...?
34742             bindEl = el.select("[tooltip]").first();
34743             var xy = ev.getXY();
34744             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34745                 //Roo.log("not in region.");
34746                 return;
34747             }
34748             //Roo.log("child element over..");
34749             
34750         }
34751         this.currentEl = el;
34752         this.currentTip.bind(bindEl);
34753         this.currentRegion = Roo.lib.Region.getRegion(dom);
34754         this.currentTip.enter();
34755         
34756     },
34757     leave : function(ev)
34758     {
34759         var dom = ev.getTarget();
34760         //Roo.log(['leave',dom]);
34761         if (!this.currentEl) {
34762             return;
34763         }
34764         
34765         
34766         if (dom != this.currentEl.dom) {
34767             return;
34768         }
34769         var xy = ev.getXY();
34770         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
34771             return;
34772         }
34773         // only activate leave if mouse cursor is outside... bounding box..
34774         
34775         
34776         
34777         
34778         if (this.currentTip) {
34779             this.currentTip.leave();
34780         }
34781         //Roo.log('clear currentEl');
34782         this.currentEl = false;
34783         
34784         
34785     },
34786     alignment : {
34787         'left' : ['r-l', [-2,0], 'right'],
34788         'right' : ['l-r', [2,0], 'left'],
34789         'bottom' : ['t-b', [0,2], 'top'],
34790         'top' : [ 'b-t', [0,-2], 'bottom']
34791     }
34792     
34793 });
34794
34795
34796 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
34797     
34798     
34799     bindEl : false,
34800     
34801     delay : null, // can be { show : 300 , hide: 500}
34802     
34803     timeout : null,
34804     
34805     hoverState : null, //???
34806     
34807     placement : 'bottom', 
34808     
34809     alignment : false,
34810     
34811     getAutoCreate : function(){
34812     
34813         var cfg = {
34814            cls : 'tooltip',   
34815            role : 'tooltip',
34816            cn : [
34817                 {
34818                     cls : 'tooltip-arrow arrow'
34819                 },
34820                 {
34821                     cls : 'tooltip-inner'
34822                 }
34823            ]
34824         };
34825         
34826         return cfg;
34827     },
34828     bind : function(el)
34829     {
34830         this.bindEl = el;
34831     },
34832     
34833     initEvents : function()
34834     {
34835         this.arrowEl = this.el.select('.arrow', true).first();
34836         this.innerEl = this.el.select('.tooltip-inner', true).first();
34837     },
34838     
34839     enter : function () {
34840        
34841         if (this.timeout != null) {
34842             clearTimeout(this.timeout);
34843         }
34844         
34845         this.hoverState = 'in';
34846          //Roo.log("enter - show");
34847         if (!this.delay || !this.delay.show) {
34848             this.show();
34849             return;
34850         }
34851         var _t = this;
34852         this.timeout = setTimeout(function () {
34853             if (_t.hoverState == 'in') {
34854                 _t.show();
34855             }
34856         }, this.delay.show);
34857     },
34858     leave : function()
34859     {
34860         clearTimeout(this.timeout);
34861     
34862         this.hoverState = 'out';
34863          if (!this.delay || !this.delay.hide) {
34864             this.hide();
34865             return;
34866         }
34867        
34868         var _t = this;
34869         this.timeout = setTimeout(function () {
34870             //Roo.log("leave - timeout");
34871             
34872             if (_t.hoverState == 'out') {
34873                 _t.hide();
34874                 Roo.bootstrap.Tooltip.currentEl = false;
34875             }
34876         }, delay);
34877     },
34878     
34879     show : function (msg)
34880     {
34881         if (!this.el) {
34882             this.render(document.body);
34883         }
34884         // set content.
34885         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34886         
34887         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34888         
34889         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34890         
34891         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34892                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34893
34894         if(this.bindEl.attr('tooltip-class')) {
34895             this.el.addClass(this.bindEl.attr('tooltip-class'));
34896         }
34897         
34898         var placement = typeof this.placement == 'function' ?
34899             this.placement.call(this, this.el, on_el) :
34900             this.placement;
34901         
34902         if(this.bindEl.attr('tooltip-placement')) {
34903             placement = this.bindEl.attr('tooltip-placement');
34904         }
34905             
34906         var autoToken = /\s?auto?\s?/i;
34907         var autoPlace = autoToken.test(placement);
34908         if (autoPlace) {
34909             placement = placement.replace(autoToken, '') || 'top';
34910         }
34911         
34912         //this.el.detach()
34913         //this.el.setXY([0,0]);
34914         this.el.show();
34915         //this.el.dom.style.display='block';
34916         
34917         //this.el.appendTo(on_el);
34918         
34919         var p = this.getPosition();
34920         var box = this.el.getBox();
34921         
34922         if (autoPlace) {
34923             // fixme..
34924         }
34925         
34926         var align = this.alignment[placement];
34927         
34928         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34929         
34930         if(placement == 'top' || placement == 'bottom'){
34931             if(xy[0] < 0){
34932                 placement = 'right';
34933             }
34934             
34935             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34936                 placement = 'left';
34937             }
34938             
34939             var scroll = Roo.select('body', true).first().getScroll();
34940             
34941             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34942                 placement = 'top';
34943             }
34944             
34945             align = this.alignment[placement];
34946             
34947             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34948             
34949         }
34950         
34951         var elems = document.getElementsByTagName('div');
34952         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34953         for (var i = 0; i < elems.length; i++) {
34954           var zindex = Number.parseInt(
34955                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34956                 10
34957           );
34958           if (zindex > highest) {
34959             highest = zindex;
34960           }
34961         }
34962         
34963         
34964         
34965         this.el.dom.style.zIndex = highest;
34966         
34967         this.el.alignTo(this.bindEl, align[0],align[1]);
34968         //var arrow = this.el.select('.arrow',true).first();
34969         //arrow.set(align[2], 
34970         
34971         this.el.addClass(placement);
34972         this.el.addClass("bs-tooltip-"+ placement);
34973         
34974         this.el.addClass('in fade show');
34975         
34976         this.hoverState = null;
34977         
34978         if (this.el.hasClass('fade')) {
34979             // fade it?
34980         }
34981         
34982         
34983         
34984         
34985         
34986     },
34987     hide : function()
34988     {
34989          
34990         if (!this.el) {
34991             return;
34992         }
34993         //this.el.setXY([0,0]);
34994         if(this.bindEl.attr('tooltip-class')) {
34995             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34996         }
34997         this.el.removeClass(['show', 'in']);
34998         //this.el.hide();
34999         
35000     }
35001     
35002 });
35003  
35004
35005  /*
35006  * - LGPL
35007  *
35008  * Location Picker
35009  * 
35010  */
35011
35012 /**
35013  * @class Roo.bootstrap.LocationPicker
35014  * @extends Roo.bootstrap.Component
35015  * Bootstrap LocationPicker class
35016  * @cfg {Number} latitude Position when init default 0
35017  * @cfg {Number} longitude Position when init default 0
35018  * @cfg {Number} zoom default 15
35019  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35020  * @cfg {Boolean} mapTypeControl default false
35021  * @cfg {Boolean} disableDoubleClickZoom default false
35022  * @cfg {Boolean} scrollwheel default true
35023  * @cfg {Boolean} streetViewControl default false
35024  * @cfg {Number} radius default 0
35025  * @cfg {String} locationName
35026  * @cfg {Boolean} draggable default true
35027  * @cfg {Boolean} enableAutocomplete default false
35028  * @cfg {Boolean} enableReverseGeocode default true
35029  * @cfg {String} markerTitle
35030  * 
35031  * @constructor
35032  * Create a new LocationPicker
35033  * @param {Object} config The config object
35034  */
35035
35036
35037 Roo.bootstrap.LocationPicker = function(config){
35038     
35039     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35040     
35041     this.addEvents({
35042         /**
35043          * @event initial
35044          * Fires when the picker initialized.
35045          * @param {Roo.bootstrap.LocationPicker} this
35046          * @param {Google Location} location
35047          */
35048         initial : true,
35049         /**
35050          * @event positionchanged
35051          * Fires when the picker position changed.
35052          * @param {Roo.bootstrap.LocationPicker} this
35053          * @param {Google Location} location
35054          */
35055         positionchanged : true,
35056         /**
35057          * @event resize
35058          * Fires when the map resize.
35059          * @param {Roo.bootstrap.LocationPicker} this
35060          */
35061         resize : true,
35062         /**
35063          * @event show
35064          * Fires when the map show.
35065          * @param {Roo.bootstrap.LocationPicker} this
35066          */
35067         show : true,
35068         /**
35069          * @event hide
35070          * Fires when the map hide.
35071          * @param {Roo.bootstrap.LocationPicker} this
35072          */
35073         hide : true,
35074         /**
35075          * @event mapClick
35076          * Fires when click the map.
35077          * @param {Roo.bootstrap.LocationPicker} this
35078          * @param {Map event} e
35079          */
35080         mapClick : true,
35081         /**
35082          * @event mapRightClick
35083          * Fires when right click the map.
35084          * @param {Roo.bootstrap.LocationPicker} this
35085          * @param {Map event} e
35086          */
35087         mapRightClick : true,
35088         /**
35089          * @event markerClick
35090          * Fires when click the marker.
35091          * @param {Roo.bootstrap.LocationPicker} this
35092          * @param {Map event} e
35093          */
35094         markerClick : true,
35095         /**
35096          * @event markerRightClick
35097          * Fires when right click the marker.
35098          * @param {Roo.bootstrap.LocationPicker} this
35099          * @param {Map event} e
35100          */
35101         markerRightClick : true,
35102         /**
35103          * @event OverlayViewDraw
35104          * Fires when OverlayView Draw
35105          * @param {Roo.bootstrap.LocationPicker} this
35106          */
35107         OverlayViewDraw : true,
35108         /**
35109          * @event OverlayViewOnAdd
35110          * Fires when OverlayView Draw
35111          * @param {Roo.bootstrap.LocationPicker} this
35112          */
35113         OverlayViewOnAdd : true,
35114         /**
35115          * @event OverlayViewOnRemove
35116          * Fires when OverlayView Draw
35117          * @param {Roo.bootstrap.LocationPicker} this
35118          */
35119         OverlayViewOnRemove : true,
35120         /**
35121          * @event OverlayViewShow
35122          * Fires when OverlayView Draw
35123          * @param {Roo.bootstrap.LocationPicker} this
35124          * @param {Pixel} cpx
35125          */
35126         OverlayViewShow : true,
35127         /**
35128          * @event OverlayViewHide
35129          * Fires when OverlayView Draw
35130          * @param {Roo.bootstrap.LocationPicker} this
35131          */
35132         OverlayViewHide : true,
35133         /**
35134          * @event loadexception
35135          * Fires when load google lib failed.
35136          * @param {Roo.bootstrap.LocationPicker} this
35137          */
35138         loadexception : true
35139     });
35140         
35141 };
35142
35143 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
35144     
35145     gMapContext: false,
35146     
35147     latitude: 0,
35148     longitude: 0,
35149     zoom: 15,
35150     mapTypeId: false,
35151     mapTypeControl: false,
35152     disableDoubleClickZoom: false,
35153     scrollwheel: true,
35154     streetViewControl: false,
35155     radius: 0,
35156     locationName: '',
35157     draggable: true,
35158     enableAutocomplete: false,
35159     enableReverseGeocode: true,
35160     markerTitle: '',
35161     
35162     getAutoCreate: function()
35163     {
35164
35165         var cfg = {
35166             tag: 'div',
35167             cls: 'roo-location-picker'
35168         };
35169         
35170         return cfg
35171     },
35172     
35173     initEvents: function(ct, position)
35174     {       
35175         if(!this.el.getWidth() || this.isApplied()){
35176             return;
35177         }
35178         
35179         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35180         
35181         this.initial();
35182     },
35183     
35184     initial: function()
35185     {
35186         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35187             this.fireEvent('loadexception', this);
35188             return;
35189         }
35190         
35191         if(!this.mapTypeId){
35192             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35193         }
35194         
35195         this.gMapContext = this.GMapContext();
35196         
35197         this.initOverlayView();
35198         
35199         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35200         
35201         var _this = this;
35202                 
35203         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35204             _this.setPosition(_this.gMapContext.marker.position);
35205         });
35206         
35207         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35208             _this.fireEvent('mapClick', this, event);
35209             
35210         });
35211
35212         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35213             _this.fireEvent('mapRightClick', this, event);
35214             
35215         });
35216         
35217         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35218             _this.fireEvent('markerClick', this, event);
35219             
35220         });
35221
35222         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35223             _this.fireEvent('markerRightClick', this, event);
35224             
35225         });
35226         
35227         this.setPosition(this.gMapContext.location);
35228         
35229         this.fireEvent('initial', this, this.gMapContext.location);
35230     },
35231     
35232     initOverlayView: function()
35233     {
35234         var _this = this;
35235         
35236         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35237             
35238             draw: function()
35239             {
35240                 _this.fireEvent('OverlayViewDraw', _this);
35241             },
35242             
35243             onAdd: function()
35244             {
35245                 _this.fireEvent('OverlayViewOnAdd', _this);
35246             },
35247             
35248             onRemove: function()
35249             {
35250                 _this.fireEvent('OverlayViewOnRemove', _this);
35251             },
35252             
35253             show: function(cpx)
35254             {
35255                 _this.fireEvent('OverlayViewShow', _this, cpx);
35256             },
35257             
35258             hide: function()
35259             {
35260                 _this.fireEvent('OverlayViewHide', _this);
35261             }
35262             
35263         });
35264     },
35265     
35266     fromLatLngToContainerPixel: function(event)
35267     {
35268         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35269     },
35270     
35271     isApplied: function() 
35272     {
35273         return this.getGmapContext() == false ? false : true;
35274     },
35275     
35276     getGmapContext: function() 
35277     {
35278         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35279     },
35280     
35281     GMapContext: function() 
35282     {
35283         var position = new google.maps.LatLng(this.latitude, this.longitude);
35284         
35285         var _map = new google.maps.Map(this.el.dom, {
35286             center: position,
35287             zoom: this.zoom,
35288             mapTypeId: this.mapTypeId,
35289             mapTypeControl: this.mapTypeControl,
35290             disableDoubleClickZoom: this.disableDoubleClickZoom,
35291             scrollwheel: this.scrollwheel,
35292             streetViewControl: this.streetViewControl,
35293             locationName: this.locationName,
35294             draggable: this.draggable,
35295             enableAutocomplete: this.enableAutocomplete,
35296             enableReverseGeocode: this.enableReverseGeocode
35297         });
35298         
35299         var _marker = new google.maps.Marker({
35300             position: position,
35301             map: _map,
35302             title: this.markerTitle,
35303             draggable: this.draggable
35304         });
35305         
35306         return {
35307             map: _map,
35308             marker: _marker,
35309             circle: null,
35310             location: position,
35311             radius: this.radius,
35312             locationName: this.locationName,
35313             addressComponents: {
35314                 formatted_address: null,
35315                 addressLine1: null,
35316                 addressLine2: null,
35317                 streetName: null,
35318                 streetNumber: null,
35319                 city: null,
35320                 district: null,
35321                 state: null,
35322                 stateOrProvince: null
35323             },
35324             settings: this,
35325             domContainer: this.el.dom,
35326             geodecoder: new google.maps.Geocoder()
35327         };
35328     },
35329     
35330     drawCircle: function(center, radius, options) 
35331     {
35332         if (this.gMapContext.circle != null) {
35333             this.gMapContext.circle.setMap(null);
35334         }
35335         if (radius > 0) {
35336             radius *= 1;
35337             options = Roo.apply({}, options, {
35338                 strokeColor: "#0000FF",
35339                 strokeOpacity: .35,
35340                 strokeWeight: 2,
35341                 fillColor: "#0000FF",
35342                 fillOpacity: .2
35343             });
35344             
35345             options.map = this.gMapContext.map;
35346             options.radius = radius;
35347             options.center = center;
35348             this.gMapContext.circle = new google.maps.Circle(options);
35349             return this.gMapContext.circle;
35350         }
35351         
35352         return null;
35353     },
35354     
35355     setPosition: function(location) 
35356     {
35357         this.gMapContext.location = location;
35358         this.gMapContext.marker.setPosition(location);
35359         this.gMapContext.map.panTo(location);
35360         this.drawCircle(location, this.gMapContext.radius, {});
35361         
35362         var _this = this;
35363         
35364         if (this.gMapContext.settings.enableReverseGeocode) {
35365             this.gMapContext.geodecoder.geocode({
35366                 latLng: this.gMapContext.location
35367             }, function(results, status) {
35368                 
35369                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35370                     _this.gMapContext.locationName = results[0].formatted_address;
35371                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35372                     
35373                     _this.fireEvent('positionchanged', this, location);
35374                 }
35375             });
35376             
35377             return;
35378         }
35379         
35380         this.fireEvent('positionchanged', this, location);
35381     },
35382     
35383     resize: function()
35384     {
35385         google.maps.event.trigger(this.gMapContext.map, "resize");
35386         
35387         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35388         
35389         this.fireEvent('resize', this);
35390     },
35391     
35392     setPositionByLatLng: function(latitude, longitude)
35393     {
35394         this.setPosition(new google.maps.LatLng(latitude, longitude));
35395     },
35396     
35397     getCurrentPosition: function() 
35398     {
35399         return {
35400             latitude: this.gMapContext.location.lat(),
35401             longitude: this.gMapContext.location.lng()
35402         };
35403     },
35404     
35405     getAddressName: function() 
35406     {
35407         return this.gMapContext.locationName;
35408     },
35409     
35410     getAddressComponents: function() 
35411     {
35412         return this.gMapContext.addressComponents;
35413     },
35414     
35415     address_component_from_google_geocode: function(address_components) 
35416     {
35417         var result = {};
35418         
35419         for (var i = 0; i < address_components.length; i++) {
35420             var component = address_components[i];
35421             if (component.types.indexOf("postal_code") >= 0) {
35422                 result.postalCode = component.short_name;
35423             } else if (component.types.indexOf("street_number") >= 0) {
35424                 result.streetNumber = component.short_name;
35425             } else if (component.types.indexOf("route") >= 0) {
35426                 result.streetName = component.short_name;
35427             } else if (component.types.indexOf("neighborhood") >= 0) {
35428                 result.city = component.short_name;
35429             } else if (component.types.indexOf("locality") >= 0) {
35430                 result.city = component.short_name;
35431             } else if (component.types.indexOf("sublocality") >= 0) {
35432                 result.district = component.short_name;
35433             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35434                 result.stateOrProvince = component.short_name;
35435             } else if (component.types.indexOf("country") >= 0) {
35436                 result.country = component.short_name;
35437             }
35438         }
35439         
35440         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35441         result.addressLine2 = "";
35442         return result;
35443     },
35444     
35445     setZoomLevel: function(zoom)
35446     {
35447         this.gMapContext.map.setZoom(zoom);
35448     },
35449     
35450     show: function()
35451     {
35452         if(!this.el){
35453             return;
35454         }
35455         
35456         this.el.show();
35457         
35458         this.resize();
35459         
35460         this.fireEvent('show', this);
35461     },
35462     
35463     hide: function()
35464     {
35465         if(!this.el){
35466             return;
35467         }
35468         
35469         this.el.hide();
35470         
35471         this.fireEvent('hide', this);
35472     }
35473     
35474 });
35475
35476 Roo.apply(Roo.bootstrap.LocationPicker, {
35477     
35478     OverlayView : function(map, options)
35479     {
35480         options = options || {};
35481         
35482         this.setMap(map);
35483     }
35484     
35485     
35486 });/**
35487  * @class Roo.bootstrap.Alert
35488  * @extends Roo.bootstrap.Component
35489  * Bootstrap Alert class - shows an alert area box
35490  * eg
35491  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35492   Enter a valid email address
35493 </div>
35494  * @licence LGPL
35495  * @cfg {String} title The title of alert
35496  * @cfg {String} html The content of alert
35497  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35498  * @cfg {String} fa font-awesomeicon
35499  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35500  * @cfg {Boolean} close true to show a x closer
35501  * 
35502  * 
35503  * @constructor
35504  * Create a new alert
35505  * @param {Object} config The config object
35506  */
35507
35508
35509 Roo.bootstrap.Alert = function(config){
35510     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35511     
35512 };
35513
35514 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35515     
35516     title: '',
35517     html: '',
35518     weight: false,
35519     fa: false,
35520     faicon: false, // BC
35521     close : false,
35522     
35523     
35524     getAutoCreate : function()
35525     {
35526         
35527         var cfg = {
35528             tag : 'div',
35529             cls : 'alert',
35530             cn : [
35531                 {
35532                     tag: 'button',
35533                     type :  "button",
35534                     cls: "close",
35535                     html : '×',
35536                     style : this.close ? '' : 'display:none'
35537                 },
35538                 {
35539                     tag : 'i',
35540                     cls : 'roo-alert-icon'
35541                     
35542                 },
35543                 {
35544                     tag : 'b',
35545                     cls : 'roo-alert-title',
35546                     html : this.title
35547                 },
35548                 {
35549                     tag : 'span',
35550                     cls : 'roo-alert-text',
35551                     html : this.html
35552                 }
35553             ]
35554         };
35555         
35556         if(this.faicon){
35557             cfg.cn[0].cls += ' fa ' + this.faicon;
35558         }
35559         if(this.fa){
35560             cfg.cn[0].cls += ' fa ' + this.fa;
35561         }
35562         
35563         if(this.weight){
35564             cfg.cls += ' alert-' + this.weight;
35565         }
35566         
35567         return cfg;
35568     },
35569     
35570     initEvents: function() 
35571     {
35572         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35573         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35574         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35575         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35576         if (this.seconds > 0) {
35577             this.hide.defer(this.seconds, this);
35578         }
35579     },
35580     /**
35581      * Set the Title Message HTML
35582      * @param {String} html
35583      */
35584     setTitle : function(str)
35585     {
35586         this.titleEl.dom.innerHTML = str;
35587     },
35588      
35589      /**
35590      * Set the Body Message HTML
35591      * @param {String} html
35592      */
35593     setHtml : function(str)
35594     {
35595         this.htmlEl.dom.innerHTML = str;
35596     },
35597     /**
35598      * Set the Weight of the alert
35599      * @param {String} (success|info|warning|danger) weight
35600      */
35601     
35602     setWeight : function(weight)
35603     {
35604         if(this.weight){
35605             this.el.removeClass('alert-' + this.weight);
35606         }
35607         
35608         this.weight = weight;
35609         
35610         this.el.addClass('alert-' + this.weight);
35611     },
35612       /**
35613      * Set the Icon of the alert
35614      * @param {String} see fontawsome names (name without the 'fa-' bit)
35615      */
35616     setIcon : function(icon)
35617     {
35618         if(this.faicon){
35619             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35620         }
35621         
35622         this.faicon = icon;
35623         
35624         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35625     },
35626     /**
35627      * Hide the Alert
35628      */
35629     hide: function() 
35630     {
35631         this.el.hide();   
35632     },
35633     /**
35634      * Show the Alert
35635      */
35636     show: function() 
35637     {  
35638         this.el.show();   
35639     }
35640     
35641 });
35642
35643  
35644 /*
35645 * Licence: LGPL
35646 */
35647
35648 /**
35649  * @class Roo.bootstrap.UploadCropbox
35650  * @extends Roo.bootstrap.Component
35651  * Bootstrap UploadCropbox class
35652  * @cfg {String} emptyText show when image has been loaded
35653  * @cfg {String} rotateNotify show when image too small to rotate
35654  * @cfg {Number} errorTimeout default 3000
35655  * @cfg {Number} minWidth default 300
35656  * @cfg {Number} minHeight default 300
35657  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35658  * @cfg {Boolean} isDocument (true|false) default false
35659  * @cfg {String} url action url
35660  * @cfg {String} paramName default 'imageUpload'
35661  * @cfg {String} method default POST
35662  * @cfg {Boolean} loadMask (true|false) default true
35663  * @cfg {Boolean} loadingText default 'Loading...'
35664  * 
35665  * @constructor
35666  * Create a new UploadCropbox
35667  * @param {Object} config The config object
35668  */
35669
35670 Roo.bootstrap.UploadCropbox = function(config){
35671     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35672     
35673     this.addEvents({
35674         /**
35675          * @event beforeselectfile
35676          * Fire before select file
35677          * @param {Roo.bootstrap.UploadCropbox} this
35678          */
35679         "beforeselectfile" : true,
35680         /**
35681          * @event initial
35682          * Fire after initEvent
35683          * @param {Roo.bootstrap.UploadCropbox} this
35684          */
35685         "initial" : true,
35686         /**
35687          * @event crop
35688          * Fire after initEvent
35689          * @param {Roo.bootstrap.UploadCropbox} this
35690          * @param {String} data
35691          */
35692         "crop" : true,
35693         /**
35694          * @event prepare
35695          * Fire when preparing the file data
35696          * @param {Roo.bootstrap.UploadCropbox} this
35697          * @param {Object} file
35698          */
35699         "prepare" : true,
35700         /**
35701          * @event exception
35702          * Fire when get exception
35703          * @param {Roo.bootstrap.UploadCropbox} this
35704          * @param {XMLHttpRequest} xhr
35705          */
35706         "exception" : true,
35707         /**
35708          * @event beforeloadcanvas
35709          * Fire before load the canvas
35710          * @param {Roo.bootstrap.UploadCropbox} this
35711          * @param {String} src
35712          */
35713         "beforeloadcanvas" : true,
35714         /**
35715          * @event trash
35716          * Fire when trash image
35717          * @param {Roo.bootstrap.UploadCropbox} this
35718          */
35719         "trash" : true,
35720         /**
35721          * @event download
35722          * Fire when download the image
35723          * @param {Roo.bootstrap.UploadCropbox} this
35724          */
35725         "download" : true,
35726         /**
35727          * @event footerbuttonclick
35728          * Fire when footerbuttonclick
35729          * @param {Roo.bootstrap.UploadCropbox} this
35730          * @param {String} type
35731          */
35732         "footerbuttonclick" : true,
35733         /**
35734          * @event resize
35735          * Fire when resize
35736          * @param {Roo.bootstrap.UploadCropbox} this
35737          */
35738         "resize" : true,
35739         /**
35740          * @event rotate
35741          * Fire when rotate the image
35742          * @param {Roo.bootstrap.UploadCropbox} this
35743          * @param {String} pos
35744          */
35745         "rotate" : true,
35746         /**
35747          * @event inspect
35748          * Fire when inspect the file
35749          * @param {Roo.bootstrap.UploadCropbox} this
35750          * @param {Object} file
35751          */
35752         "inspect" : true,
35753         /**
35754          * @event upload
35755          * Fire when xhr upload the file
35756          * @param {Roo.bootstrap.UploadCropbox} this
35757          * @param {Object} data
35758          */
35759         "upload" : true,
35760         /**
35761          * @event arrange
35762          * Fire when arrange the file data
35763          * @param {Roo.bootstrap.UploadCropbox} this
35764          * @param {Object} formData
35765          */
35766         "arrange" : true
35767     });
35768     
35769     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35770 };
35771
35772 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
35773     
35774     emptyText : 'Click to upload image',
35775     rotateNotify : 'Image is too small to rotate',
35776     errorTimeout : 3000,
35777     scale : 0,
35778     baseScale : 1,
35779     rotate : 0,
35780     dragable : false,
35781     pinching : false,
35782     mouseX : 0,
35783     mouseY : 0,
35784     cropData : false,
35785     minWidth : 300,
35786     minHeight : 300,
35787     file : false,
35788     exif : {},
35789     baseRotate : 1,
35790     cropType : 'image/jpeg',
35791     buttons : false,
35792     canvasLoaded : false,
35793     isDocument : false,
35794     method : 'POST',
35795     paramName : 'imageUpload',
35796     loadMask : true,
35797     loadingText : 'Loading...',
35798     maskEl : false,
35799     
35800     getAutoCreate : function()
35801     {
35802         var cfg = {
35803             tag : 'div',
35804             cls : 'roo-upload-cropbox',
35805             cn : [
35806                 {
35807                     tag : 'input',
35808                     cls : 'roo-upload-cropbox-selector',
35809                     type : 'file'
35810                 },
35811                 {
35812                     tag : 'div',
35813                     cls : 'roo-upload-cropbox-body',
35814                     style : 'cursor:pointer',
35815                     cn : [
35816                         {
35817                             tag : 'div',
35818                             cls : 'roo-upload-cropbox-preview'
35819                         },
35820                         {
35821                             tag : 'div',
35822                             cls : 'roo-upload-cropbox-thumb'
35823                         },
35824                         {
35825                             tag : 'div',
35826                             cls : 'roo-upload-cropbox-empty-notify',
35827                             html : this.emptyText
35828                         },
35829                         {
35830                             tag : 'div',
35831                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35832                             html : this.rotateNotify
35833                         }
35834                     ]
35835                 },
35836                 {
35837                     tag : 'div',
35838                     cls : 'roo-upload-cropbox-footer',
35839                     cn : {
35840                         tag : 'div',
35841                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35842                         cn : []
35843                     }
35844                 }
35845             ]
35846         };
35847         
35848         return cfg;
35849     },
35850     
35851     onRender : function(ct, position)
35852     {
35853         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35854         
35855         if (this.buttons.length) {
35856             
35857             Roo.each(this.buttons, function(bb) {
35858                 
35859                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35860                 
35861                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35862                 
35863             }, this);
35864         }
35865         
35866         if(this.loadMask){
35867             this.maskEl = this.el;
35868         }
35869     },
35870     
35871     initEvents : function()
35872     {
35873         this.urlAPI = (window.createObjectURL && window) || 
35874                                 (window.URL && URL.revokeObjectURL && URL) || 
35875                                 (window.webkitURL && webkitURL);
35876                         
35877         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35878         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35879         
35880         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35881         this.selectorEl.hide();
35882         
35883         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35884         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35885         
35886         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35887         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35888         this.thumbEl.hide();
35889         
35890         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35891         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35892         
35893         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35894         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35895         this.errorEl.hide();
35896         
35897         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35898         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35899         this.footerEl.hide();
35900         
35901         this.setThumbBoxSize();
35902         
35903         this.bind();
35904         
35905         this.resize();
35906         
35907         this.fireEvent('initial', this);
35908     },
35909
35910     bind : function()
35911     {
35912         var _this = this;
35913         
35914         window.addEventListener("resize", function() { _this.resize(); } );
35915         
35916         this.bodyEl.on('click', this.beforeSelectFile, this);
35917         
35918         if(Roo.isTouch){
35919             this.bodyEl.on('touchstart', this.onTouchStart, this);
35920             this.bodyEl.on('touchmove', this.onTouchMove, this);
35921             this.bodyEl.on('touchend', this.onTouchEnd, this);
35922         }
35923         
35924         if(!Roo.isTouch){
35925             this.bodyEl.on('mousedown', this.onMouseDown, this);
35926             this.bodyEl.on('mousemove', this.onMouseMove, this);
35927             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35928             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35929             Roo.get(document).on('mouseup', this.onMouseUp, this);
35930         }
35931         
35932         this.selectorEl.on('change', this.onFileSelected, this);
35933     },
35934     
35935     reset : function()
35936     {    
35937         this.scale = 0;
35938         this.baseScale = 1;
35939         this.rotate = 0;
35940         this.baseRotate = 1;
35941         this.dragable = false;
35942         this.pinching = false;
35943         this.mouseX = 0;
35944         this.mouseY = 0;
35945         this.cropData = false;
35946         this.notifyEl.dom.innerHTML = this.emptyText;
35947         
35948         this.selectorEl.dom.value = '';
35949         
35950     },
35951     
35952     resize : function()
35953     {
35954         if(this.fireEvent('resize', this) != false){
35955             this.setThumbBoxPosition();
35956             this.setCanvasPosition();
35957         }
35958     },
35959     
35960     onFooterButtonClick : function(e, el, o, type)
35961     {
35962         switch (type) {
35963             case 'rotate-left' :
35964                 this.onRotateLeft(e);
35965                 break;
35966             case 'rotate-right' :
35967                 this.onRotateRight(e);
35968                 break;
35969             case 'picture' :
35970                 this.beforeSelectFile(e);
35971                 break;
35972             case 'trash' :
35973                 this.trash(e);
35974                 break;
35975             case 'crop' :
35976                 this.crop(e);
35977                 break;
35978             case 'download' :
35979                 this.download(e);
35980                 break;
35981             default :
35982                 break;
35983         }
35984         
35985         this.fireEvent('footerbuttonclick', this, type);
35986     },
35987     
35988     beforeSelectFile : function(e)
35989     {
35990         e.preventDefault();
35991         
35992         if(this.fireEvent('beforeselectfile', this) != false){
35993             this.selectorEl.dom.click();
35994         }
35995     },
35996     
35997     onFileSelected : function(e)
35998     {
35999         e.preventDefault();
36000         
36001         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36002             return;
36003         }
36004         
36005         var file = this.selectorEl.dom.files[0];
36006         
36007         if(this.fireEvent('inspect', this, file) != false){
36008             this.prepare(file);
36009         }
36010         
36011     },
36012     
36013     trash : function(e)
36014     {
36015         this.fireEvent('trash', this);
36016     },
36017     
36018     download : function(e)
36019     {
36020         this.fireEvent('download', this);
36021     },
36022     
36023     loadCanvas : function(src)
36024     {   
36025         if(this.fireEvent('beforeloadcanvas', this, src) != false){
36026             
36027             this.reset();
36028             
36029             this.imageEl = document.createElement('img');
36030             
36031             var _this = this;
36032             
36033             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36034             
36035             this.imageEl.src = src;
36036         }
36037     },
36038     
36039     onLoadCanvas : function()
36040     {   
36041         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36042         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36043         
36044         this.bodyEl.un('click', this.beforeSelectFile, this);
36045         
36046         this.notifyEl.hide();
36047         this.thumbEl.show();
36048         this.footerEl.show();
36049         
36050         this.baseRotateLevel();
36051         
36052         if(this.isDocument){
36053             this.setThumbBoxSize();
36054         }
36055         
36056         this.setThumbBoxPosition();
36057         
36058         this.baseScaleLevel();
36059         
36060         this.draw();
36061         
36062         this.resize();
36063         
36064         this.canvasLoaded = true;
36065         
36066         if(this.loadMask){
36067             this.maskEl.unmask();
36068         }
36069         
36070     },
36071     
36072     setCanvasPosition : function()
36073     {   
36074         if(!this.canvasEl){
36075             return;
36076         }
36077         
36078         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36079         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36080         
36081         this.previewEl.setLeft(pw);
36082         this.previewEl.setTop(ph);
36083         
36084     },
36085     
36086     onMouseDown : function(e)
36087     {   
36088         e.stopEvent();
36089         
36090         this.dragable = true;
36091         this.pinching = false;
36092         
36093         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36094             this.dragable = false;
36095             return;
36096         }
36097         
36098         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36099         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36100         
36101     },
36102     
36103     onMouseMove : function(e)
36104     {   
36105         e.stopEvent();
36106         
36107         if(!this.canvasLoaded){
36108             return;
36109         }
36110         
36111         if (!this.dragable){
36112             return;
36113         }
36114         
36115         var minX = Math.ceil(this.thumbEl.getLeft(true));
36116         var minY = Math.ceil(this.thumbEl.getTop(true));
36117         
36118         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36119         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36120         
36121         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36122         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36123         
36124         x = x - this.mouseX;
36125         y = y - this.mouseY;
36126         
36127         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36128         var bgY = Math.ceil(y + this.previewEl.getTop(true));
36129         
36130         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36131         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36132         
36133         this.previewEl.setLeft(bgX);
36134         this.previewEl.setTop(bgY);
36135         
36136         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36137         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36138     },
36139     
36140     onMouseUp : function(e)
36141     {   
36142         e.stopEvent();
36143         
36144         this.dragable = false;
36145     },
36146     
36147     onMouseWheel : function(e)
36148     {   
36149         e.stopEvent();
36150         
36151         this.startScale = this.scale;
36152         
36153         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36154         
36155         if(!this.zoomable()){
36156             this.scale = this.startScale;
36157             return;
36158         }
36159         
36160         this.draw();
36161         
36162         return;
36163     },
36164     
36165     zoomable : function()
36166     {
36167         var minScale = this.thumbEl.getWidth() / this.minWidth;
36168         
36169         if(this.minWidth < this.minHeight){
36170             minScale = this.thumbEl.getHeight() / this.minHeight;
36171         }
36172         
36173         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36174         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36175         
36176         if(
36177                 this.isDocument &&
36178                 (this.rotate == 0 || this.rotate == 180) && 
36179                 (
36180                     width > this.imageEl.OriginWidth || 
36181                     height > this.imageEl.OriginHeight ||
36182                     (width < this.minWidth && height < this.minHeight)
36183                 )
36184         ){
36185             return false;
36186         }
36187         
36188         if(
36189                 this.isDocument &&
36190                 (this.rotate == 90 || this.rotate == 270) && 
36191                 (
36192                     width > this.imageEl.OriginWidth || 
36193                     height > this.imageEl.OriginHeight ||
36194                     (width < this.minHeight && height < this.minWidth)
36195                 )
36196         ){
36197             return false;
36198         }
36199         
36200         if(
36201                 !this.isDocument &&
36202                 (this.rotate == 0 || this.rotate == 180) && 
36203                 (
36204                     width < this.minWidth || 
36205                     width > this.imageEl.OriginWidth || 
36206                     height < this.minHeight || 
36207                     height > this.imageEl.OriginHeight
36208                 )
36209         ){
36210             return false;
36211         }
36212         
36213         if(
36214                 !this.isDocument &&
36215                 (this.rotate == 90 || this.rotate == 270) && 
36216                 (
36217                     width < this.minHeight || 
36218                     width > this.imageEl.OriginWidth || 
36219                     height < this.minWidth || 
36220                     height > this.imageEl.OriginHeight
36221                 )
36222         ){
36223             return false;
36224         }
36225         
36226         return true;
36227         
36228     },
36229     
36230     onRotateLeft : function(e)
36231     {   
36232         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36233             
36234             var minScale = this.thumbEl.getWidth() / this.minWidth;
36235             
36236             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36237             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36238             
36239             this.startScale = this.scale;
36240             
36241             while (this.getScaleLevel() < minScale){
36242             
36243                 this.scale = this.scale + 1;
36244                 
36245                 if(!this.zoomable()){
36246                     break;
36247                 }
36248                 
36249                 if(
36250                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36251                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36252                 ){
36253                     continue;
36254                 }
36255                 
36256                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36257
36258                 this.draw();
36259                 
36260                 return;
36261             }
36262             
36263             this.scale = this.startScale;
36264             
36265             this.onRotateFail();
36266             
36267             return false;
36268         }
36269         
36270         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36271
36272         if(this.isDocument){
36273             this.setThumbBoxSize();
36274             this.setThumbBoxPosition();
36275             this.setCanvasPosition();
36276         }
36277         
36278         this.draw();
36279         
36280         this.fireEvent('rotate', this, 'left');
36281         
36282     },
36283     
36284     onRotateRight : function(e)
36285     {
36286         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36287             
36288             var minScale = this.thumbEl.getWidth() / this.minWidth;
36289         
36290             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36291             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36292             
36293             this.startScale = this.scale;
36294             
36295             while (this.getScaleLevel() < minScale){
36296             
36297                 this.scale = this.scale + 1;
36298                 
36299                 if(!this.zoomable()){
36300                     break;
36301                 }
36302                 
36303                 if(
36304                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36305                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36306                 ){
36307                     continue;
36308                 }
36309                 
36310                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36311
36312                 this.draw();
36313                 
36314                 return;
36315             }
36316             
36317             this.scale = this.startScale;
36318             
36319             this.onRotateFail();
36320             
36321             return false;
36322         }
36323         
36324         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36325
36326         if(this.isDocument){
36327             this.setThumbBoxSize();
36328             this.setThumbBoxPosition();
36329             this.setCanvasPosition();
36330         }
36331         
36332         this.draw();
36333         
36334         this.fireEvent('rotate', this, 'right');
36335     },
36336     
36337     onRotateFail : function()
36338     {
36339         this.errorEl.show(true);
36340         
36341         var _this = this;
36342         
36343         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36344     },
36345     
36346     draw : function()
36347     {
36348         this.previewEl.dom.innerHTML = '';
36349         
36350         var canvasEl = document.createElement("canvas");
36351         
36352         var contextEl = canvasEl.getContext("2d");
36353         
36354         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36355         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36356         var center = this.imageEl.OriginWidth / 2;
36357         
36358         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36359             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36360             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36361             center = this.imageEl.OriginHeight / 2;
36362         }
36363         
36364         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36365         
36366         contextEl.translate(center, center);
36367         contextEl.rotate(this.rotate * Math.PI / 180);
36368
36369         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36370         
36371         this.canvasEl = document.createElement("canvas");
36372         
36373         this.contextEl = this.canvasEl.getContext("2d");
36374         
36375         switch (this.rotate) {
36376             case 0 :
36377                 
36378                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36379                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36380                 
36381                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36382                 
36383                 break;
36384             case 90 : 
36385                 
36386                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36387                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36388                 
36389                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36390                     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);
36391                     break;
36392                 }
36393                 
36394                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36395                 
36396                 break;
36397             case 180 :
36398                 
36399                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36400                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36401                 
36402                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36403                     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);
36404                     break;
36405                 }
36406                 
36407                 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);
36408                 
36409                 break;
36410             case 270 :
36411                 
36412                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36413                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36414         
36415                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36416                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36417                     break;
36418                 }
36419                 
36420                 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);
36421                 
36422                 break;
36423             default : 
36424                 break;
36425         }
36426         
36427         this.previewEl.appendChild(this.canvasEl);
36428         
36429         this.setCanvasPosition();
36430     },
36431     
36432     crop : function()
36433     {
36434         if(!this.canvasLoaded){
36435             return;
36436         }
36437         
36438         var imageCanvas = document.createElement("canvas");
36439         
36440         var imageContext = imageCanvas.getContext("2d");
36441         
36442         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36443         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36444         
36445         var center = imageCanvas.width / 2;
36446         
36447         imageContext.translate(center, center);
36448         
36449         imageContext.rotate(this.rotate * Math.PI / 180);
36450         
36451         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36452         
36453         var canvas = document.createElement("canvas");
36454         
36455         var context = canvas.getContext("2d");
36456                 
36457         canvas.width = this.minWidth;
36458         canvas.height = this.minHeight;
36459
36460         switch (this.rotate) {
36461             case 0 :
36462                 
36463                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36464                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36465                 
36466                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36467                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36468                 
36469                 var targetWidth = this.minWidth - 2 * x;
36470                 var targetHeight = this.minHeight - 2 * y;
36471                 
36472                 var scale = 1;
36473                 
36474                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36475                     scale = targetWidth / width;
36476                 }
36477                 
36478                 if(x > 0 && y == 0){
36479                     scale = targetHeight / height;
36480                 }
36481                 
36482                 if(x > 0 && y > 0){
36483                     scale = targetWidth / width;
36484                     
36485                     if(width < height){
36486                         scale = targetHeight / height;
36487                     }
36488                 }
36489                 
36490                 context.scale(scale, scale);
36491                 
36492                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36493                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36494
36495                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36496                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36497
36498                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36499                 
36500                 break;
36501             case 90 : 
36502                 
36503                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36504                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36505                 
36506                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36507                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36508                 
36509                 var targetWidth = this.minWidth - 2 * x;
36510                 var targetHeight = this.minHeight - 2 * y;
36511                 
36512                 var scale = 1;
36513                 
36514                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36515                     scale = targetWidth / width;
36516                 }
36517                 
36518                 if(x > 0 && y == 0){
36519                     scale = targetHeight / height;
36520                 }
36521                 
36522                 if(x > 0 && y > 0){
36523                     scale = targetWidth / width;
36524                     
36525                     if(width < height){
36526                         scale = targetHeight / height;
36527                     }
36528                 }
36529                 
36530                 context.scale(scale, scale);
36531                 
36532                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36533                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36534
36535                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36536                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36537                 
36538                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36539                 
36540                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36541                 
36542                 break;
36543             case 180 :
36544                 
36545                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36546                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36547                 
36548                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36549                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36550                 
36551                 var targetWidth = this.minWidth - 2 * x;
36552                 var targetHeight = this.minHeight - 2 * y;
36553                 
36554                 var scale = 1;
36555                 
36556                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36557                     scale = targetWidth / width;
36558                 }
36559                 
36560                 if(x > 0 && y == 0){
36561                     scale = targetHeight / height;
36562                 }
36563                 
36564                 if(x > 0 && y > 0){
36565                     scale = targetWidth / width;
36566                     
36567                     if(width < height){
36568                         scale = targetHeight / height;
36569                     }
36570                 }
36571                 
36572                 context.scale(scale, scale);
36573                 
36574                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36575                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36576
36577                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36578                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36579
36580                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36581                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36582                 
36583                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36584                 
36585                 break;
36586             case 270 :
36587                 
36588                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36589                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36590                 
36591                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36592                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36593                 
36594                 var targetWidth = this.minWidth - 2 * x;
36595                 var targetHeight = this.minHeight - 2 * y;
36596                 
36597                 var scale = 1;
36598                 
36599                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36600                     scale = targetWidth / width;
36601                 }
36602                 
36603                 if(x > 0 && y == 0){
36604                     scale = targetHeight / height;
36605                 }
36606                 
36607                 if(x > 0 && y > 0){
36608                     scale = targetWidth / width;
36609                     
36610                     if(width < height){
36611                         scale = targetHeight / height;
36612                     }
36613                 }
36614                 
36615                 context.scale(scale, scale);
36616                 
36617                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36618                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36619
36620                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36621                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36622                 
36623                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36624                 
36625                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36626                 
36627                 break;
36628             default : 
36629                 break;
36630         }
36631         
36632         this.cropData = canvas.toDataURL(this.cropType);
36633         
36634         if(this.fireEvent('crop', this, this.cropData) !== false){
36635             this.process(this.file, this.cropData);
36636         }
36637         
36638         return;
36639         
36640     },
36641     
36642     setThumbBoxSize : function()
36643     {
36644         var width, height;
36645         
36646         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36647             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36648             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36649             
36650             this.minWidth = width;
36651             this.minHeight = height;
36652             
36653             if(this.rotate == 90 || this.rotate == 270){
36654                 this.minWidth = height;
36655                 this.minHeight = width;
36656             }
36657         }
36658         
36659         height = 300;
36660         width = Math.ceil(this.minWidth * height / this.minHeight);
36661         
36662         if(this.minWidth > this.minHeight){
36663             width = 300;
36664             height = Math.ceil(this.minHeight * width / this.minWidth);
36665         }
36666         
36667         this.thumbEl.setStyle({
36668             width : width + 'px',
36669             height : height + 'px'
36670         });
36671
36672         return;
36673             
36674     },
36675     
36676     setThumbBoxPosition : function()
36677     {
36678         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36679         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36680         
36681         this.thumbEl.setLeft(x);
36682         this.thumbEl.setTop(y);
36683         
36684     },
36685     
36686     baseRotateLevel : function()
36687     {
36688         this.baseRotate = 1;
36689         
36690         if(
36691                 typeof(this.exif) != 'undefined' &&
36692                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36693                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36694         ){
36695             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36696         }
36697         
36698         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36699         
36700     },
36701     
36702     baseScaleLevel : function()
36703     {
36704         var width, height;
36705         
36706         if(this.isDocument){
36707             
36708             if(this.baseRotate == 6 || this.baseRotate == 8){
36709             
36710                 height = this.thumbEl.getHeight();
36711                 this.baseScale = height / this.imageEl.OriginWidth;
36712
36713                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36714                     width = this.thumbEl.getWidth();
36715                     this.baseScale = width / this.imageEl.OriginHeight;
36716                 }
36717
36718                 return;
36719             }
36720
36721             height = this.thumbEl.getHeight();
36722             this.baseScale = height / this.imageEl.OriginHeight;
36723
36724             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36725                 width = this.thumbEl.getWidth();
36726                 this.baseScale = width / this.imageEl.OriginWidth;
36727             }
36728
36729             return;
36730         }
36731         
36732         if(this.baseRotate == 6 || this.baseRotate == 8){
36733             
36734             width = this.thumbEl.getHeight();
36735             this.baseScale = width / this.imageEl.OriginHeight;
36736             
36737             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36738                 height = this.thumbEl.getWidth();
36739                 this.baseScale = height / this.imageEl.OriginHeight;
36740             }
36741             
36742             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36743                 height = this.thumbEl.getWidth();
36744                 this.baseScale = height / this.imageEl.OriginHeight;
36745                 
36746                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36747                     width = this.thumbEl.getHeight();
36748                     this.baseScale = width / this.imageEl.OriginWidth;
36749                 }
36750             }
36751             
36752             return;
36753         }
36754         
36755         width = this.thumbEl.getWidth();
36756         this.baseScale = width / this.imageEl.OriginWidth;
36757         
36758         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36759             height = this.thumbEl.getHeight();
36760             this.baseScale = height / this.imageEl.OriginHeight;
36761         }
36762         
36763         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36764             
36765             height = this.thumbEl.getHeight();
36766             this.baseScale = height / this.imageEl.OriginHeight;
36767             
36768             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36769                 width = this.thumbEl.getWidth();
36770                 this.baseScale = width / this.imageEl.OriginWidth;
36771             }
36772             
36773         }
36774         
36775         return;
36776     },
36777     
36778     getScaleLevel : function()
36779     {
36780         return this.baseScale * Math.pow(1.1, this.scale);
36781     },
36782     
36783     onTouchStart : function(e)
36784     {
36785         if(!this.canvasLoaded){
36786             this.beforeSelectFile(e);
36787             return;
36788         }
36789         
36790         var touches = e.browserEvent.touches;
36791         
36792         if(!touches){
36793             return;
36794         }
36795         
36796         if(touches.length == 1){
36797             this.onMouseDown(e);
36798             return;
36799         }
36800         
36801         if(touches.length != 2){
36802             return;
36803         }
36804         
36805         var coords = [];
36806         
36807         for(var i = 0, finger; finger = touches[i]; i++){
36808             coords.push(finger.pageX, finger.pageY);
36809         }
36810         
36811         var x = Math.pow(coords[0] - coords[2], 2);
36812         var y = Math.pow(coords[1] - coords[3], 2);
36813         
36814         this.startDistance = Math.sqrt(x + y);
36815         
36816         this.startScale = this.scale;
36817         
36818         this.pinching = true;
36819         this.dragable = false;
36820         
36821     },
36822     
36823     onTouchMove : function(e)
36824     {
36825         if(!this.pinching && !this.dragable){
36826             return;
36827         }
36828         
36829         var touches = e.browserEvent.touches;
36830         
36831         if(!touches){
36832             return;
36833         }
36834         
36835         if(this.dragable){
36836             this.onMouseMove(e);
36837             return;
36838         }
36839         
36840         var coords = [];
36841         
36842         for(var i = 0, finger; finger = touches[i]; i++){
36843             coords.push(finger.pageX, finger.pageY);
36844         }
36845         
36846         var x = Math.pow(coords[0] - coords[2], 2);
36847         var y = Math.pow(coords[1] - coords[3], 2);
36848         
36849         this.endDistance = Math.sqrt(x + y);
36850         
36851         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36852         
36853         if(!this.zoomable()){
36854             this.scale = this.startScale;
36855             return;
36856         }
36857         
36858         this.draw();
36859         
36860     },
36861     
36862     onTouchEnd : function(e)
36863     {
36864         this.pinching = false;
36865         this.dragable = false;
36866         
36867     },
36868     
36869     process : function(file, crop)
36870     {
36871         if(this.loadMask){
36872             this.maskEl.mask(this.loadingText);
36873         }
36874         
36875         this.xhr = new XMLHttpRequest();
36876         
36877         file.xhr = this.xhr;
36878
36879         this.xhr.open(this.method, this.url, true);
36880         
36881         var headers = {
36882             "Accept": "application/json",
36883             "Cache-Control": "no-cache",
36884             "X-Requested-With": "XMLHttpRequest"
36885         };
36886         
36887         for (var headerName in headers) {
36888             var headerValue = headers[headerName];
36889             if (headerValue) {
36890                 this.xhr.setRequestHeader(headerName, headerValue);
36891             }
36892         }
36893         
36894         var _this = this;
36895         
36896         this.xhr.onload = function()
36897         {
36898             _this.xhrOnLoad(_this.xhr);
36899         }
36900         
36901         this.xhr.onerror = function()
36902         {
36903             _this.xhrOnError(_this.xhr);
36904         }
36905         
36906         var formData = new FormData();
36907
36908         formData.append('returnHTML', 'NO');
36909         
36910         if(crop){
36911             formData.append('crop', crop);
36912         }
36913         
36914         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36915             formData.append(this.paramName, file, file.name);
36916         }
36917         
36918         if(typeof(file.filename) != 'undefined'){
36919             formData.append('filename', file.filename);
36920         }
36921         
36922         if(typeof(file.mimetype) != 'undefined'){
36923             formData.append('mimetype', file.mimetype);
36924         }
36925         
36926         if(this.fireEvent('arrange', this, formData) != false){
36927             this.xhr.send(formData);
36928         };
36929     },
36930     
36931     xhrOnLoad : function(xhr)
36932     {
36933         if(this.loadMask){
36934             this.maskEl.unmask();
36935         }
36936         
36937         if (xhr.readyState !== 4) {
36938             this.fireEvent('exception', this, xhr);
36939             return;
36940         }
36941
36942         var response = Roo.decode(xhr.responseText);
36943         
36944         if(!response.success){
36945             this.fireEvent('exception', this, xhr);
36946             return;
36947         }
36948         
36949         var response = Roo.decode(xhr.responseText);
36950         
36951         this.fireEvent('upload', this, response);
36952         
36953     },
36954     
36955     xhrOnError : function()
36956     {
36957         if(this.loadMask){
36958             this.maskEl.unmask();
36959         }
36960         
36961         Roo.log('xhr on error');
36962         
36963         var response = Roo.decode(xhr.responseText);
36964           
36965         Roo.log(response);
36966         
36967     },
36968     
36969     prepare : function(file)
36970     {   
36971         if(this.loadMask){
36972             this.maskEl.mask(this.loadingText);
36973         }
36974         
36975         this.file = false;
36976         this.exif = {};
36977         
36978         if(typeof(file) === 'string'){
36979             this.loadCanvas(file);
36980             return;
36981         }
36982         
36983         if(!file || !this.urlAPI){
36984             return;
36985         }
36986         
36987         this.file = file;
36988         this.cropType = file.type;
36989         
36990         var _this = this;
36991         
36992         if(this.fireEvent('prepare', this, this.file) != false){
36993             
36994             var reader = new FileReader();
36995             
36996             reader.onload = function (e) {
36997                 if (e.target.error) {
36998                     Roo.log(e.target.error);
36999                     return;
37000                 }
37001                 
37002                 var buffer = e.target.result,
37003                     dataView = new DataView(buffer),
37004                     offset = 2,
37005                     maxOffset = dataView.byteLength - 4,
37006                     markerBytes,
37007                     markerLength;
37008                 
37009                 if (dataView.getUint16(0) === 0xffd8) {
37010                     while (offset < maxOffset) {
37011                         markerBytes = dataView.getUint16(offset);
37012                         
37013                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37014                             markerLength = dataView.getUint16(offset + 2) + 2;
37015                             if (offset + markerLength > dataView.byteLength) {
37016                                 Roo.log('Invalid meta data: Invalid segment size.');
37017                                 break;
37018                             }
37019                             
37020                             if(markerBytes == 0xffe1){
37021                                 _this.parseExifData(
37022                                     dataView,
37023                                     offset,
37024                                     markerLength
37025                                 );
37026                             }
37027                             
37028                             offset += markerLength;
37029                             
37030                             continue;
37031                         }
37032                         
37033                         break;
37034                     }
37035                     
37036                 }
37037                 
37038                 var url = _this.urlAPI.createObjectURL(_this.file);
37039                 
37040                 _this.loadCanvas(url);
37041                 
37042                 return;
37043             }
37044             
37045             reader.readAsArrayBuffer(this.file);
37046             
37047         }
37048         
37049     },
37050     
37051     parseExifData : function(dataView, offset, length)
37052     {
37053         var tiffOffset = offset + 10,
37054             littleEndian,
37055             dirOffset;
37056     
37057         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37058             // No Exif data, might be XMP data instead
37059             return;
37060         }
37061         
37062         // Check for the ASCII code for "Exif" (0x45786966):
37063         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37064             // No Exif data, might be XMP data instead
37065             return;
37066         }
37067         if (tiffOffset + 8 > dataView.byteLength) {
37068             Roo.log('Invalid Exif data: Invalid segment size.');
37069             return;
37070         }
37071         // Check for the two null bytes:
37072         if (dataView.getUint16(offset + 8) !== 0x0000) {
37073             Roo.log('Invalid Exif data: Missing byte alignment offset.');
37074             return;
37075         }
37076         // Check the byte alignment:
37077         switch (dataView.getUint16(tiffOffset)) {
37078         case 0x4949:
37079             littleEndian = true;
37080             break;
37081         case 0x4D4D:
37082             littleEndian = false;
37083             break;
37084         default:
37085             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37086             return;
37087         }
37088         // Check for the TIFF tag marker (0x002A):
37089         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37090             Roo.log('Invalid Exif data: Missing TIFF marker.');
37091             return;
37092         }
37093         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37094         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37095         
37096         this.parseExifTags(
37097             dataView,
37098             tiffOffset,
37099             tiffOffset + dirOffset,
37100             littleEndian
37101         );
37102     },
37103     
37104     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37105     {
37106         var tagsNumber,
37107             dirEndOffset,
37108             i;
37109         if (dirOffset + 6 > dataView.byteLength) {
37110             Roo.log('Invalid Exif data: Invalid directory offset.');
37111             return;
37112         }
37113         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37114         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37115         if (dirEndOffset + 4 > dataView.byteLength) {
37116             Roo.log('Invalid Exif data: Invalid directory size.');
37117             return;
37118         }
37119         for (i = 0; i < tagsNumber; i += 1) {
37120             this.parseExifTag(
37121                 dataView,
37122                 tiffOffset,
37123                 dirOffset + 2 + 12 * i, // tag offset
37124                 littleEndian
37125             );
37126         }
37127         // Return the offset to the next directory:
37128         return dataView.getUint32(dirEndOffset, littleEndian);
37129     },
37130     
37131     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
37132     {
37133         var tag = dataView.getUint16(offset, littleEndian);
37134         
37135         this.exif[tag] = this.getExifValue(
37136             dataView,
37137             tiffOffset,
37138             offset,
37139             dataView.getUint16(offset + 2, littleEndian), // tag type
37140             dataView.getUint32(offset + 4, littleEndian), // tag length
37141             littleEndian
37142         );
37143     },
37144     
37145     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37146     {
37147         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37148             tagSize,
37149             dataOffset,
37150             values,
37151             i,
37152             str,
37153             c;
37154     
37155         if (!tagType) {
37156             Roo.log('Invalid Exif data: Invalid tag type.');
37157             return;
37158         }
37159         
37160         tagSize = tagType.size * length;
37161         // Determine if the value is contained in the dataOffset bytes,
37162         // or if the value at the dataOffset is a pointer to the actual data:
37163         dataOffset = tagSize > 4 ?
37164                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37165         if (dataOffset + tagSize > dataView.byteLength) {
37166             Roo.log('Invalid Exif data: Invalid data offset.');
37167             return;
37168         }
37169         if (length === 1) {
37170             return tagType.getValue(dataView, dataOffset, littleEndian);
37171         }
37172         values = [];
37173         for (i = 0; i < length; i += 1) {
37174             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37175         }
37176         
37177         if (tagType.ascii) {
37178             str = '';
37179             // Concatenate the chars:
37180             for (i = 0; i < values.length; i += 1) {
37181                 c = values[i];
37182                 // Ignore the terminating NULL byte(s):
37183                 if (c === '\u0000') {
37184                     break;
37185                 }
37186                 str += c;
37187             }
37188             return str;
37189         }
37190         return values;
37191     }
37192     
37193 });
37194
37195 Roo.apply(Roo.bootstrap.UploadCropbox, {
37196     tags : {
37197         'Orientation': 0x0112
37198     },
37199     
37200     Orientation: {
37201             1: 0, //'top-left',
37202 //            2: 'top-right',
37203             3: 180, //'bottom-right',
37204 //            4: 'bottom-left',
37205 //            5: 'left-top',
37206             6: 90, //'right-top',
37207 //            7: 'right-bottom',
37208             8: 270 //'left-bottom'
37209     },
37210     
37211     exifTagTypes : {
37212         // byte, 8-bit unsigned int:
37213         1: {
37214             getValue: function (dataView, dataOffset) {
37215                 return dataView.getUint8(dataOffset);
37216             },
37217             size: 1
37218         },
37219         // ascii, 8-bit byte:
37220         2: {
37221             getValue: function (dataView, dataOffset) {
37222                 return String.fromCharCode(dataView.getUint8(dataOffset));
37223             },
37224             size: 1,
37225             ascii: true
37226         },
37227         // short, 16 bit int:
37228         3: {
37229             getValue: function (dataView, dataOffset, littleEndian) {
37230                 return dataView.getUint16(dataOffset, littleEndian);
37231             },
37232             size: 2
37233         },
37234         // long, 32 bit int:
37235         4: {
37236             getValue: function (dataView, dataOffset, littleEndian) {
37237                 return dataView.getUint32(dataOffset, littleEndian);
37238             },
37239             size: 4
37240         },
37241         // rational = two long values, first is numerator, second is denominator:
37242         5: {
37243             getValue: function (dataView, dataOffset, littleEndian) {
37244                 return dataView.getUint32(dataOffset, littleEndian) /
37245                     dataView.getUint32(dataOffset + 4, littleEndian);
37246             },
37247             size: 8
37248         },
37249         // slong, 32 bit signed int:
37250         9: {
37251             getValue: function (dataView, dataOffset, littleEndian) {
37252                 return dataView.getInt32(dataOffset, littleEndian);
37253             },
37254             size: 4
37255         },
37256         // srational, two slongs, first is numerator, second is denominator:
37257         10: {
37258             getValue: function (dataView, dataOffset, littleEndian) {
37259                 return dataView.getInt32(dataOffset, littleEndian) /
37260                     dataView.getInt32(dataOffset + 4, littleEndian);
37261             },
37262             size: 8
37263         }
37264     },
37265     
37266     footer : {
37267         STANDARD : [
37268             {
37269                 tag : 'div',
37270                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37271                 action : 'rotate-left',
37272                 cn : [
37273                     {
37274                         tag : 'button',
37275                         cls : 'btn btn-default',
37276                         html : '<i class="fa fa-undo"></i>'
37277                     }
37278                 ]
37279             },
37280             {
37281                 tag : 'div',
37282                 cls : 'btn-group roo-upload-cropbox-picture',
37283                 action : 'picture',
37284                 cn : [
37285                     {
37286                         tag : 'button',
37287                         cls : 'btn btn-default',
37288                         html : '<i class="fa fa-picture-o"></i>'
37289                     }
37290                 ]
37291             },
37292             {
37293                 tag : 'div',
37294                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37295                 action : 'rotate-right',
37296                 cn : [
37297                     {
37298                         tag : 'button',
37299                         cls : 'btn btn-default',
37300                         html : '<i class="fa fa-repeat"></i>'
37301                     }
37302                 ]
37303             }
37304         ],
37305         DOCUMENT : [
37306             {
37307                 tag : 'div',
37308                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37309                 action : 'rotate-left',
37310                 cn : [
37311                     {
37312                         tag : 'button',
37313                         cls : 'btn btn-default',
37314                         html : '<i class="fa fa-undo"></i>'
37315                     }
37316                 ]
37317             },
37318             {
37319                 tag : 'div',
37320                 cls : 'btn-group roo-upload-cropbox-download',
37321                 action : 'download',
37322                 cn : [
37323                     {
37324                         tag : 'button',
37325                         cls : 'btn btn-default',
37326                         html : '<i class="fa fa-download"></i>'
37327                     }
37328                 ]
37329             },
37330             {
37331                 tag : 'div',
37332                 cls : 'btn-group roo-upload-cropbox-crop',
37333                 action : 'crop',
37334                 cn : [
37335                     {
37336                         tag : 'button',
37337                         cls : 'btn btn-default',
37338                         html : '<i class="fa fa-crop"></i>'
37339                     }
37340                 ]
37341             },
37342             {
37343                 tag : 'div',
37344                 cls : 'btn-group roo-upload-cropbox-trash',
37345                 action : 'trash',
37346                 cn : [
37347                     {
37348                         tag : 'button',
37349                         cls : 'btn btn-default',
37350                         html : '<i class="fa fa-trash"></i>'
37351                     }
37352                 ]
37353             },
37354             {
37355                 tag : 'div',
37356                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37357                 action : 'rotate-right',
37358                 cn : [
37359                     {
37360                         tag : 'button',
37361                         cls : 'btn btn-default',
37362                         html : '<i class="fa fa-repeat"></i>'
37363                     }
37364                 ]
37365             }
37366         ],
37367         ROTATOR : [
37368             {
37369                 tag : 'div',
37370                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37371                 action : 'rotate-left',
37372                 cn : [
37373                     {
37374                         tag : 'button',
37375                         cls : 'btn btn-default',
37376                         html : '<i class="fa fa-undo"></i>'
37377                     }
37378                 ]
37379             },
37380             {
37381                 tag : 'div',
37382                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37383                 action : 'rotate-right',
37384                 cn : [
37385                     {
37386                         tag : 'button',
37387                         cls : 'btn btn-default',
37388                         html : '<i class="fa fa-repeat"></i>'
37389                     }
37390                 ]
37391             }
37392         ]
37393     }
37394 });
37395
37396 /*
37397 * Licence: LGPL
37398 */
37399
37400 /**
37401  * @class Roo.bootstrap.DocumentManager
37402  * @extends Roo.bootstrap.Component
37403  * Bootstrap DocumentManager class
37404  * @cfg {String} paramName default 'imageUpload'
37405  * @cfg {String} toolTipName default 'filename'
37406  * @cfg {String} method default POST
37407  * @cfg {String} url action url
37408  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37409  * @cfg {Boolean} multiple multiple upload default true
37410  * @cfg {Number} thumbSize default 300
37411  * @cfg {String} fieldLabel
37412  * @cfg {Number} labelWidth default 4
37413  * @cfg {String} labelAlign (left|top) default left
37414  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37415 * @cfg {Number} labellg set the width of label (1-12)
37416  * @cfg {Number} labelmd set the width of label (1-12)
37417  * @cfg {Number} labelsm set the width of label (1-12)
37418  * @cfg {Number} labelxs set the width of label (1-12)
37419  * 
37420  * @constructor
37421  * Create a new DocumentManager
37422  * @param {Object} config The config object
37423  */
37424
37425 Roo.bootstrap.DocumentManager = function(config){
37426     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37427     
37428     this.files = [];
37429     this.delegates = [];
37430     
37431     this.addEvents({
37432         /**
37433          * @event initial
37434          * Fire when initial the DocumentManager
37435          * @param {Roo.bootstrap.DocumentManager} this
37436          */
37437         "initial" : true,
37438         /**
37439          * @event inspect
37440          * inspect selected file
37441          * @param {Roo.bootstrap.DocumentManager} this
37442          * @param {File} file
37443          */
37444         "inspect" : true,
37445         /**
37446          * @event exception
37447          * Fire when xhr load exception
37448          * @param {Roo.bootstrap.DocumentManager} this
37449          * @param {XMLHttpRequest} xhr
37450          */
37451         "exception" : true,
37452         /**
37453          * @event afterupload
37454          * Fire when xhr load exception
37455          * @param {Roo.bootstrap.DocumentManager} this
37456          * @param {XMLHttpRequest} xhr
37457          */
37458         "afterupload" : true,
37459         /**
37460          * @event prepare
37461          * prepare the form data
37462          * @param {Roo.bootstrap.DocumentManager} this
37463          * @param {Object} formData
37464          */
37465         "prepare" : true,
37466         /**
37467          * @event remove
37468          * Fire when remove the file
37469          * @param {Roo.bootstrap.DocumentManager} this
37470          * @param {Object} file
37471          */
37472         "remove" : true,
37473         /**
37474          * @event refresh
37475          * Fire after refresh the file
37476          * @param {Roo.bootstrap.DocumentManager} this
37477          */
37478         "refresh" : true,
37479         /**
37480          * @event click
37481          * Fire after click the image
37482          * @param {Roo.bootstrap.DocumentManager} this
37483          * @param {Object} file
37484          */
37485         "click" : true,
37486         /**
37487          * @event edit
37488          * Fire when upload a image and editable set to true
37489          * @param {Roo.bootstrap.DocumentManager} this
37490          * @param {Object} file
37491          */
37492         "edit" : true,
37493         /**
37494          * @event beforeselectfile
37495          * Fire before select file
37496          * @param {Roo.bootstrap.DocumentManager} this
37497          */
37498         "beforeselectfile" : true,
37499         /**
37500          * @event process
37501          * Fire before process file
37502          * @param {Roo.bootstrap.DocumentManager} this
37503          * @param {Object} file
37504          */
37505         "process" : true,
37506         /**
37507          * @event previewrendered
37508          * Fire when preview rendered
37509          * @param {Roo.bootstrap.DocumentManager} this
37510          * @param {Object} file
37511          */
37512         "previewrendered" : true,
37513         /**
37514          */
37515         "previewResize" : true
37516         
37517     });
37518 };
37519
37520 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37521     
37522     boxes : 0,
37523     inputName : '',
37524     thumbSize : 300,
37525     multiple : true,
37526     files : false,
37527     method : 'POST',
37528     url : '',
37529     paramName : 'imageUpload',
37530     toolTipName : 'filename',
37531     fieldLabel : '',
37532     labelWidth : 4,
37533     labelAlign : 'left',
37534     editable : true,
37535     delegates : false,
37536     xhr : false, 
37537     
37538     labellg : 0,
37539     labelmd : 0,
37540     labelsm : 0,
37541     labelxs : 0,
37542     
37543     getAutoCreate : function()
37544     {   
37545         var managerWidget = {
37546             tag : 'div',
37547             cls : 'roo-document-manager',
37548             cn : [
37549                 {
37550                     tag : 'input',
37551                     cls : 'roo-document-manager-selector',
37552                     type : 'file'
37553                 },
37554                 {
37555                     tag : 'div',
37556                     cls : 'roo-document-manager-uploader',
37557                     cn : [
37558                         {
37559                             tag : 'div',
37560                             cls : 'roo-document-manager-upload-btn',
37561                             html : '<i class="fa fa-plus"></i>'
37562                         }
37563                     ]
37564                     
37565                 }
37566             ]
37567         };
37568         
37569         var content = [
37570             {
37571                 tag : 'div',
37572                 cls : 'column col-md-12',
37573                 cn : managerWidget
37574             }
37575         ];
37576         
37577         if(this.fieldLabel.length){
37578             
37579             content = [
37580                 {
37581                     tag : 'div',
37582                     cls : 'column col-md-12',
37583                     html : this.fieldLabel
37584                 },
37585                 {
37586                     tag : 'div',
37587                     cls : 'column col-md-12',
37588                     cn : managerWidget
37589                 }
37590             ];
37591
37592             if(this.labelAlign == 'left'){
37593                 content = [
37594                     {
37595                         tag : 'div',
37596                         cls : 'column',
37597                         html : this.fieldLabel
37598                     },
37599                     {
37600                         tag : 'div',
37601                         cls : 'column',
37602                         cn : managerWidget
37603                     }
37604                 ];
37605                 
37606                 if(this.labelWidth > 12){
37607                     content[0].style = "width: " + this.labelWidth + 'px';
37608                 }
37609
37610                 if(this.labelWidth < 13 && this.labelmd == 0){
37611                     this.labelmd = this.labelWidth;
37612                 }
37613
37614                 if(this.labellg > 0){
37615                     content[0].cls += ' col-lg-' + this.labellg;
37616                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37617                 }
37618
37619                 if(this.labelmd > 0){
37620                     content[0].cls += ' col-md-' + this.labelmd;
37621                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37622                 }
37623
37624                 if(this.labelsm > 0){
37625                     content[0].cls += ' col-sm-' + this.labelsm;
37626                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37627                 }
37628
37629                 if(this.labelxs > 0){
37630                     content[0].cls += ' col-xs-' + this.labelxs;
37631                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37632                 }
37633                 
37634             }
37635         }
37636         
37637         var cfg = {
37638             tag : 'div',
37639             cls : 'row clearfix',
37640             cn : content
37641         };
37642         
37643         return cfg;
37644         
37645     },
37646     
37647     initEvents : function()
37648     {
37649         this.managerEl = this.el.select('.roo-document-manager', true).first();
37650         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37651         
37652         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37653         this.selectorEl.hide();
37654         
37655         if(this.multiple){
37656             this.selectorEl.attr('multiple', 'multiple');
37657         }
37658         
37659         this.selectorEl.on('change', this.onFileSelected, this);
37660         
37661         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37662         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37663         
37664         this.uploader.on('click', this.onUploaderClick, this);
37665         
37666         this.renderProgressDialog();
37667         
37668         var _this = this;
37669         
37670         window.addEventListener("resize", function() { _this.refresh(); } );
37671         
37672         this.fireEvent('initial', this);
37673     },
37674     
37675     renderProgressDialog : function()
37676     {
37677         var _this = this;
37678         
37679         this.progressDialog = new Roo.bootstrap.Modal({
37680             cls : 'roo-document-manager-progress-dialog',
37681             allow_close : false,
37682             animate : false,
37683             title : '',
37684             buttons : [
37685                 {
37686                     name  :'cancel',
37687                     weight : 'danger',
37688                     html : 'Cancel'
37689                 }
37690             ], 
37691             listeners : { 
37692                 btnclick : function() {
37693                     _this.uploadCancel();
37694                     this.hide();
37695                 }
37696             }
37697         });
37698          
37699         this.progressDialog.render(Roo.get(document.body));
37700          
37701         this.progress = new Roo.bootstrap.Progress({
37702             cls : 'roo-document-manager-progress',
37703             active : true,
37704             striped : true
37705         });
37706         
37707         this.progress.render(this.progressDialog.getChildContainer());
37708         
37709         this.progressBar = new Roo.bootstrap.ProgressBar({
37710             cls : 'roo-document-manager-progress-bar',
37711             aria_valuenow : 0,
37712             aria_valuemin : 0,
37713             aria_valuemax : 12,
37714             panel : 'success'
37715         });
37716         
37717         this.progressBar.render(this.progress.getChildContainer());
37718     },
37719     
37720     onUploaderClick : function(e)
37721     {
37722         e.preventDefault();
37723      
37724         if(this.fireEvent('beforeselectfile', this) != false){
37725             this.selectorEl.dom.click();
37726         }
37727         
37728     },
37729     
37730     onFileSelected : function(e)
37731     {
37732         e.preventDefault();
37733         
37734         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37735             return;
37736         }
37737         
37738         Roo.each(this.selectorEl.dom.files, function(file){
37739             if(this.fireEvent('inspect', this, file) != false){
37740                 this.files.push(file);
37741             }
37742         }, this);
37743         
37744         this.queue();
37745         
37746     },
37747     
37748     queue : function()
37749     {
37750         this.selectorEl.dom.value = '';
37751         
37752         if(!this.files || !this.files.length){
37753             return;
37754         }
37755         
37756         if(this.boxes > 0 && this.files.length > this.boxes){
37757             this.files = this.files.slice(0, this.boxes);
37758         }
37759         
37760         this.uploader.show();
37761         
37762         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37763             this.uploader.hide();
37764         }
37765         
37766         var _this = this;
37767         
37768         var files = [];
37769         
37770         var docs = [];
37771         
37772         Roo.each(this.files, function(file){
37773             
37774             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37775                 var f = this.renderPreview(file);
37776                 files.push(f);
37777                 return;
37778             }
37779             
37780             if(file.type.indexOf('image') != -1){
37781                 this.delegates.push(
37782                     (function(){
37783                         _this.process(file);
37784                     }).createDelegate(this)
37785                 );
37786         
37787                 return;
37788             }
37789             
37790             docs.push(
37791                 (function(){
37792                     _this.process(file);
37793                 }).createDelegate(this)
37794             );
37795             
37796         }, this);
37797         
37798         this.files = files;
37799         
37800         this.delegates = this.delegates.concat(docs);
37801         
37802         if(!this.delegates.length){
37803             this.refresh();
37804             return;
37805         }
37806         
37807         this.progressBar.aria_valuemax = this.delegates.length;
37808         
37809         this.arrange();
37810         
37811         return;
37812     },
37813     
37814     arrange : function()
37815     {
37816         if(!this.delegates.length){
37817             this.progressDialog.hide();
37818             this.refresh();
37819             return;
37820         }
37821         
37822         var delegate = this.delegates.shift();
37823         
37824         this.progressDialog.show();
37825         
37826         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37827         
37828         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37829         
37830         delegate();
37831     },
37832     
37833     refresh : function()
37834     {
37835         this.uploader.show();
37836         
37837         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37838             this.uploader.hide();
37839         }
37840         
37841         Roo.isTouch ? this.closable(false) : this.closable(true);
37842         
37843         this.fireEvent('refresh', this);
37844     },
37845     
37846     onRemove : function(e, el, o)
37847     {
37848         e.preventDefault();
37849         
37850         this.fireEvent('remove', this, o);
37851         
37852     },
37853     
37854     remove : function(o)
37855     {
37856         var files = [];
37857         
37858         Roo.each(this.files, function(file){
37859             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37860                 files.push(file);
37861                 return;
37862             }
37863
37864             o.target.remove();
37865
37866         }, this);
37867         
37868         this.files = files;
37869         
37870         this.refresh();
37871     },
37872     
37873     clear : function()
37874     {
37875         Roo.each(this.files, function(file){
37876             if(!file.target){
37877                 return;
37878             }
37879             
37880             file.target.remove();
37881
37882         }, this);
37883         
37884         this.files = [];
37885         
37886         this.refresh();
37887     },
37888     
37889     onClick : function(e, el, o)
37890     {
37891         e.preventDefault();
37892         
37893         this.fireEvent('click', this, o);
37894         
37895     },
37896     
37897     closable : function(closable)
37898     {
37899         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37900             
37901             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37902             
37903             if(closable){
37904                 el.show();
37905                 return;
37906             }
37907             
37908             el.hide();
37909             
37910         }, this);
37911     },
37912     
37913     xhrOnLoad : function(xhr)
37914     {
37915         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37916             el.remove();
37917         }, this);
37918         
37919         if (xhr.readyState !== 4) {
37920             this.arrange();
37921             this.fireEvent('exception', this, xhr);
37922             return;
37923         }
37924
37925         var response = Roo.decode(xhr.responseText);
37926         
37927         if(!response.success){
37928             this.arrange();
37929             this.fireEvent('exception', this, xhr);
37930             return;
37931         }
37932         
37933         var file = this.renderPreview(response.data);
37934         
37935         this.files.push(file);
37936         
37937         this.arrange();
37938         
37939         this.fireEvent('afterupload', this, xhr);
37940         
37941     },
37942     
37943     xhrOnError : function(xhr)
37944     {
37945         Roo.log('xhr on error');
37946         
37947         var response = Roo.decode(xhr.responseText);
37948           
37949         Roo.log(response);
37950         
37951         this.arrange();
37952     },
37953     
37954     process : function(file)
37955     {
37956         if(this.fireEvent('process', this, file) !== false){
37957             if(this.editable && file.type.indexOf('image') != -1){
37958                 this.fireEvent('edit', this, file);
37959                 return;
37960             }
37961
37962             this.uploadStart(file, false);
37963
37964             return;
37965         }
37966         
37967     },
37968     
37969     uploadStart : function(file, crop)
37970     {
37971         this.xhr = new XMLHttpRequest();
37972         
37973         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37974             this.arrange();
37975             return;
37976         }
37977         
37978         file.xhr = this.xhr;
37979             
37980         this.managerEl.createChild({
37981             tag : 'div',
37982             cls : 'roo-document-manager-loading',
37983             cn : [
37984                 {
37985                     tag : 'div',
37986                     tooltip : file.name,
37987                     cls : 'roo-document-manager-thumb',
37988                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37989                 }
37990             ]
37991
37992         });
37993
37994         this.xhr.open(this.method, this.url, true);
37995         
37996         var headers = {
37997             "Accept": "application/json",
37998             "Cache-Control": "no-cache",
37999             "X-Requested-With": "XMLHttpRequest"
38000         };
38001         
38002         for (var headerName in headers) {
38003             var headerValue = headers[headerName];
38004             if (headerValue) {
38005                 this.xhr.setRequestHeader(headerName, headerValue);
38006             }
38007         }
38008         
38009         var _this = this;
38010         
38011         this.xhr.onload = function()
38012         {
38013             _this.xhrOnLoad(_this.xhr);
38014         }
38015         
38016         this.xhr.onerror = function()
38017         {
38018             _this.xhrOnError(_this.xhr);
38019         }
38020         
38021         var formData = new FormData();
38022
38023         formData.append('returnHTML', 'NO');
38024         
38025         if(crop){
38026             formData.append('crop', crop);
38027         }
38028         
38029         formData.append(this.paramName, file, file.name);
38030         
38031         var options = {
38032             file : file, 
38033             manually : false
38034         };
38035         
38036         if(this.fireEvent('prepare', this, formData, options) != false){
38037             
38038             if(options.manually){
38039                 return;
38040             }
38041             
38042             this.xhr.send(formData);
38043             return;
38044         };
38045         
38046         this.uploadCancel();
38047     },
38048     
38049     uploadCancel : function()
38050     {
38051         if (this.xhr) {
38052             this.xhr.abort();
38053         }
38054         
38055         this.delegates = [];
38056         
38057         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38058             el.remove();
38059         }, this);
38060         
38061         this.arrange();
38062     },
38063     
38064     renderPreview : function(file)
38065     {
38066         if(typeof(file.target) != 'undefined' && file.target){
38067             return file;
38068         }
38069         
38070         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38071         
38072         var previewEl = this.managerEl.createChild({
38073             tag : 'div',
38074             cls : 'roo-document-manager-preview',
38075             cn : [
38076                 {
38077                     tag : 'div',
38078                     tooltip : file[this.toolTipName],
38079                     cls : 'roo-document-manager-thumb',
38080                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38081                 },
38082                 {
38083                     tag : 'button',
38084                     cls : 'close',
38085                     html : '<i class="fa fa-times-circle"></i>'
38086                 }
38087             ]
38088         });
38089
38090         var close = previewEl.select('button.close', true).first();
38091
38092         close.on('click', this.onRemove, this, file);
38093
38094         file.target = previewEl;
38095
38096         var image = previewEl.select('img', true).first();
38097         
38098         var _this = this;
38099         
38100         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38101         
38102         image.on('click', this.onClick, this, file);
38103         
38104         this.fireEvent('previewrendered', this, file);
38105         
38106         return file;
38107         
38108     },
38109     
38110     onPreviewLoad : function(file, image)
38111     {
38112         if(typeof(file.target) == 'undefined' || !file.target){
38113             return;
38114         }
38115         
38116         var width = image.dom.naturalWidth || image.dom.width;
38117         var height = image.dom.naturalHeight || image.dom.height;
38118         
38119         if(!this.previewResize) {
38120             return;
38121         }
38122         
38123         if(width > height){
38124             file.target.addClass('wide');
38125             return;
38126         }
38127         
38128         file.target.addClass('tall');
38129         return;
38130         
38131     },
38132     
38133     uploadFromSource : function(file, crop)
38134     {
38135         this.xhr = new XMLHttpRequest();
38136         
38137         this.managerEl.createChild({
38138             tag : 'div',
38139             cls : 'roo-document-manager-loading',
38140             cn : [
38141                 {
38142                     tag : 'div',
38143                     tooltip : file.name,
38144                     cls : 'roo-document-manager-thumb',
38145                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38146                 }
38147             ]
38148
38149         });
38150
38151         this.xhr.open(this.method, this.url, true);
38152         
38153         var headers = {
38154             "Accept": "application/json",
38155             "Cache-Control": "no-cache",
38156             "X-Requested-With": "XMLHttpRequest"
38157         };
38158         
38159         for (var headerName in headers) {
38160             var headerValue = headers[headerName];
38161             if (headerValue) {
38162                 this.xhr.setRequestHeader(headerName, headerValue);
38163             }
38164         }
38165         
38166         var _this = this;
38167         
38168         this.xhr.onload = function()
38169         {
38170             _this.xhrOnLoad(_this.xhr);
38171         }
38172         
38173         this.xhr.onerror = function()
38174         {
38175             _this.xhrOnError(_this.xhr);
38176         }
38177         
38178         var formData = new FormData();
38179
38180         formData.append('returnHTML', 'NO');
38181         
38182         formData.append('crop', crop);
38183         
38184         if(typeof(file.filename) != 'undefined'){
38185             formData.append('filename', file.filename);
38186         }
38187         
38188         if(typeof(file.mimetype) != 'undefined'){
38189             formData.append('mimetype', file.mimetype);
38190         }
38191         
38192         Roo.log(formData);
38193         
38194         if(this.fireEvent('prepare', this, formData) != false){
38195             this.xhr.send(formData);
38196         };
38197     }
38198 });
38199
38200 /*
38201 * Licence: LGPL
38202 */
38203
38204 /**
38205  * @class Roo.bootstrap.DocumentViewer
38206  * @extends Roo.bootstrap.Component
38207  * Bootstrap DocumentViewer class
38208  * @cfg {Boolean} showDownload (true|false) show download button (default true)
38209  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38210  * 
38211  * @constructor
38212  * Create a new DocumentViewer
38213  * @param {Object} config The config object
38214  */
38215
38216 Roo.bootstrap.DocumentViewer = function(config){
38217     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38218     
38219     this.addEvents({
38220         /**
38221          * @event initial
38222          * Fire after initEvent
38223          * @param {Roo.bootstrap.DocumentViewer} this
38224          */
38225         "initial" : true,
38226         /**
38227          * @event click
38228          * Fire after click
38229          * @param {Roo.bootstrap.DocumentViewer} this
38230          */
38231         "click" : true,
38232         /**
38233          * @event download
38234          * Fire after download button
38235          * @param {Roo.bootstrap.DocumentViewer} this
38236          */
38237         "download" : true,
38238         /**
38239          * @event trash
38240          * Fire after trash button
38241          * @param {Roo.bootstrap.DocumentViewer} this
38242          */
38243         "trash" : true
38244         
38245     });
38246 };
38247
38248 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
38249     
38250     showDownload : true,
38251     
38252     showTrash : true,
38253     
38254     getAutoCreate : function()
38255     {
38256         var cfg = {
38257             tag : 'div',
38258             cls : 'roo-document-viewer',
38259             cn : [
38260                 {
38261                     tag : 'div',
38262                     cls : 'roo-document-viewer-body',
38263                     cn : [
38264                         {
38265                             tag : 'div',
38266                             cls : 'roo-document-viewer-thumb',
38267                             cn : [
38268                                 {
38269                                     tag : 'img',
38270                                     cls : 'roo-document-viewer-image'
38271                                 }
38272                             ]
38273                         }
38274                     ]
38275                 },
38276                 {
38277                     tag : 'div',
38278                     cls : 'roo-document-viewer-footer',
38279                     cn : {
38280                         tag : 'div',
38281                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38282                         cn : [
38283                             {
38284                                 tag : 'div',
38285                                 cls : 'btn-group roo-document-viewer-download',
38286                                 cn : [
38287                                     {
38288                                         tag : 'button',
38289                                         cls : 'btn btn-default',
38290                                         html : '<i class="fa fa-download"></i>'
38291                                     }
38292                                 ]
38293                             },
38294                             {
38295                                 tag : 'div',
38296                                 cls : 'btn-group roo-document-viewer-trash',
38297                                 cn : [
38298                                     {
38299                                         tag : 'button',
38300                                         cls : 'btn btn-default',
38301                                         html : '<i class="fa fa-trash"></i>'
38302                                     }
38303                                 ]
38304                             }
38305                         ]
38306                     }
38307                 }
38308             ]
38309         };
38310         
38311         return cfg;
38312     },
38313     
38314     initEvents : function()
38315     {
38316         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38317         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38318         
38319         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38320         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38321         
38322         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38323         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38324         
38325         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38326         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38327         
38328         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38329         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38330         
38331         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38332         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38333         
38334         this.bodyEl.on('click', this.onClick, this);
38335         this.downloadBtn.on('click', this.onDownload, this);
38336         this.trashBtn.on('click', this.onTrash, this);
38337         
38338         this.downloadBtn.hide();
38339         this.trashBtn.hide();
38340         
38341         if(this.showDownload){
38342             this.downloadBtn.show();
38343         }
38344         
38345         if(this.showTrash){
38346             this.trashBtn.show();
38347         }
38348         
38349         if(!this.showDownload && !this.showTrash) {
38350             this.footerEl.hide();
38351         }
38352         
38353     },
38354     
38355     initial : function()
38356     {
38357         this.fireEvent('initial', this);
38358         
38359     },
38360     
38361     onClick : function(e)
38362     {
38363         e.preventDefault();
38364         
38365         this.fireEvent('click', this);
38366     },
38367     
38368     onDownload : function(e)
38369     {
38370         e.preventDefault();
38371         
38372         this.fireEvent('download', this);
38373     },
38374     
38375     onTrash : function(e)
38376     {
38377         e.preventDefault();
38378         
38379         this.fireEvent('trash', this);
38380     }
38381     
38382 });
38383 /*
38384  * - LGPL
38385  *
38386  * FieldLabel
38387  * 
38388  */
38389
38390 /**
38391  * @class Roo.bootstrap.form.FieldLabel
38392  * @extends Roo.bootstrap.Component
38393  * Bootstrap FieldLabel class
38394  * @cfg {String} html contents of the element
38395  * @cfg {String} tag tag of the element default label
38396  * @cfg {String} cls class of the element
38397  * @cfg {String} target label target 
38398  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38399  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38400  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38401  * @cfg {String} iconTooltip default "This field is required"
38402  * @cfg {String} indicatorpos (left|right) default left
38403  * 
38404  * @constructor
38405  * Create a new FieldLabel
38406  * @param {Object} config The config object
38407  */
38408
38409 Roo.bootstrap.form.FieldLabel = function(config){
38410     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38411     
38412     this.addEvents({
38413             /**
38414              * @event invalid
38415              * Fires after the field has been marked as invalid.
38416              * @param {Roo.form.FieldLabel} this
38417              * @param {String} msg The validation message
38418              */
38419             invalid : true,
38420             /**
38421              * @event valid
38422              * Fires after the field has been validated with no errors.
38423              * @param {Roo.form.FieldLabel} this
38424              */
38425             valid : true
38426         });
38427 };
38428
38429 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38430     
38431     tag: 'label',
38432     cls: '',
38433     html: '',
38434     target: '',
38435     allowBlank : true,
38436     invalidClass : 'has-warning',
38437     validClass : 'has-success',
38438     iconTooltip : 'This field is required',
38439     indicatorpos : 'left',
38440     
38441     getAutoCreate : function(){
38442         
38443         var cls = "";
38444         if (!this.allowBlank) {
38445             cls  = "visible";
38446         }
38447         
38448         var cfg = {
38449             tag : this.tag,
38450             cls : 'roo-bootstrap-field-label ' + this.cls,
38451             for : this.target,
38452             cn : [
38453                 {
38454                     tag : 'i',
38455                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38456                     tooltip : this.iconTooltip
38457                 },
38458                 {
38459                     tag : 'span',
38460                     html : this.html
38461                 }
38462             ] 
38463         };
38464         
38465         if(this.indicatorpos == 'right'){
38466             var cfg = {
38467                 tag : this.tag,
38468                 cls : 'roo-bootstrap-field-label ' + this.cls,
38469                 for : this.target,
38470                 cn : [
38471                     {
38472                         tag : 'span',
38473                         html : this.html
38474                     },
38475                     {
38476                         tag : 'i',
38477                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38478                         tooltip : this.iconTooltip
38479                     }
38480                 ] 
38481             };
38482         }
38483         
38484         return cfg;
38485     },
38486     
38487     initEvents: function() 
38488     {
38489         Roo.bootstrap.Element.superclass.initEvents.call(this);
38490         
38491         this.indicator = this.indicatorEl();
38492         
38493         if(this.indicator){
38494             this.indicator.removeClass('visible');
38495             this.indicator.addClass('invisible');
38496         }
38497         
38498         Roo.bootstrap.form.FieldLabel.register(this);
38499     },
38500     
38501     indicatorEl : function()
38502     {
38503         var indicator = this.el.select('i.roo-required-indicator',true).first();
38504         
38505         if(!indicator){
38506             return false;
38507         }
38508         
38509         return indicator;
38510         
38511     },
38512     
38513     /**
38514      * Mark this field as valid
38515      */
38516     markValid : function()
38517     {
38518         if(this.indicator){
38519             this.indicator.removeClass('visible');
38520             this.indicator.addClass('invisible');
38521         }
38522         if (Roo.bootstrap.version == 3) {
38523             this.el.removeClass(this.invalidClass);
38524             this.el.addClass(this.validClass);
38525         } else {
38526             this.el.removeClass('is-invalid');
38527             this.el.addClass('is-valid');
38528         }
38529         
38530         
38531         this.fireEvent('valid', this);
38532     },
38533     
38534     /**
38535      * Mark this field as invalid
38536      * @param {String} msg The validation message
38537      */
38538     markInvalid : function(msg)
38539     {
38540         if(this.indicator){
38541             this.indicator.removeClass('invisible');
38542             this.indicator.addClass('visible');
38543         }
38544           if (Roo.bootstrap.version == 3) {
38545             this.el.removeClass(this.validClass);
38546             this.el.addClass(this.invalidClass);
38547         } else {
38548             this.el.removeClass('is-valid');
38549             this.el.addClass('is-invalid');
38550         }
38551         
38552         
38553         this.fireEvent('invalid', this, msg);
38554     }
38555     
38556    
38557 });
38558
38559 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38560     
38561     groups: {},
38562     
38563      /**
38564     * register a FieldLabel Group
38565     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38566     */
38567     register : function(label)
38568     {
38569         if(this.groups.hasOwnProperty(label.target)){
38570             return;
38571         }
38572      
38573         this.groups[label.target] = label;
38574         
38575     },
38576     /**
38577     * fetch a FieldLabel Group based on the target
38578     * @param {string} target
38579     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38580     */
38581     get: function(target) {
38582         if (typeof(this.groups[target]) == 'undefined') {
38583             return false;
38584         }
38585         
38586         return this.groups[target] ;
38587     }
38588 });
38589
38590  
38591
38592  /*
38593  * - LGPL
38594  *
38595  * page DateSplitField.
38596  * 
38597  */
38598
38599
38600 /**
38601  * @class Roo.bootstrap.form.DateSplitField
38602  * @extends Roo.bootstrap.Component
38603  * Bootstrap DateSplitField class
38604  * @cfg {string} fieldLabel - the label associated
38605  * @cfg {Number} labelWidth set the width of label (0-12)
38606  * @cfg {String} labelAlign (top|left)
38607  * @cfg {Boolean} dayAllowBlank (true|false) default false
38608  * @cfg {Boolean} monthAllowBlank (true|false) default false
38609  * @cfg {Boolean} yearAllowBlank (true|false) default false
38610  * @cfg {string} dayPlaceholder 
38611  * @cfg {string} monthPlaceholder
38612  * @cfg {string} yearPlaceholder
38613  * @cfg {string} dayFormat default 'd'
38614  * @cfg {string} monthFormat default 'm'
38615  * @cfg {string} yearFormat default 'Y'
38616  * @cfg {Number} labellg set the width of label (1-12)
38617  * @cfg {Number} labelmd set the width of label (1-12)
38618  * @cfg {Number} labelsm set the width of label (1-12)
38619  * @cfg {Number} labelxs set the width of label (1-12)
38620
38621  *     
38622  * @constructor
38623  * Create a new DateSplitField
38624  * @param {Object} config The config object
38625  */
38626
38627 Roo.bootstrap.form.DateSplitField = function(config){
38628     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38629     
38630     this.addEvents({
38631         // raw events
38632          /**
38633          * @event years
38634          * getting the data of years
38635          * @param {Roo.bootstrap.form.DateSplitField} this
38636          * @param {Object} years
38637          */
38638         "years" : true,
38639         /**
38640          * @event days
38641          * getting the data of days
38642          * @param {Roo.bootstrap.form.DateSplitField} this
38643          * @param {Object} days
38644          */
38645         "days" : true,
38646         /**
38647          * @event invalid
38648          * Fires after the field has been marked as invalid.
38649          * @param {Roo.form.Field} this
38650          * @param {String} msg The validation message
38651          */
38652         invalid : true,
38653        /**
38654          * @event valid
38655          * Fires after the field has been validated with no errors.
38656          * @param {Roo.form.Field} this
38657          */
38658         valid : true
38659     });
38660 };
38661
38662 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38663     
38664     fieldLabel : '',
38665     labelAlign : 'top',
38666     labelWidth : 3,
38667     dayAllowBlank : false,
38668     monthAllowBlank : false,
38669     yearAllowBlank : false,
38670     dayPlaceholder : '',
38671     monthPlaceholder : '',
38672     yearPlaceholder : '',
38673     dayFormat : 'd',
38674     monthFormat : 'm',
38675     yearFormat : 'Y',
38676     isFormField : true,
38677     labellg : 0,
38678     labelmd : 0,
38679     labelsm : 0,
38680     labelxs : 0,
38681     
38682     getAutoCreate : function()
38683     {
38684         var cfg = {
38685             tag : 'div',
38686             cls : 'row roo-date-split-field-group',
38687             cn : [
38688                 {
38689                     tag : 'input',
38690                     type : 'hidden',
38691                     cls : 'form-hidden-field roo-date-split-field-group-value',
38692                     name : this.name
38693                 }
38694             ]
38695         };
38696         
38697         var labelCls = 'col-md-12';
38698         var contentCls = 'col-md-4';
38699         
38700         if(this.fieldLabel){
38701             
38702             var label = {
38703                 tag : 'div',
38704                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38705                 cn : [
38706                     {
38707                         tag : 'label',
38708                         html : this.fieldLabel
38709                     }
38710                 ]
38711             };
38712             
38713             if(this.labelAlign == 'left'){
38714             
38715                 if(this.labelWidth > 12){
38716                     label.style = "width: " + this.labelWidth + 'px';
38717                 }
38718
38719                 if(this.labelWidth < 13 && this.labelmd == 0){
38720                     this.labelmd = this.labelWidth;
38721                 }
38722
38723                 if(this.labellg > 0){
38724                     labelCls = ' col-lg-' + this.labellg;
38725                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38726                 }
38727
38728                 if(this.labelmd > 0){
38729                     labelCls = ' col-md-' + this.labelmd;
38730                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38731                 }
38732
38733                 if(this.labelsm > 0){
38734                     labelCls = ' col-sm-' + this.labelsm;
38735                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38736                 }
38737
38738                 if(this.labelxs > 0){
38739                     labelCls = ' col-xs-' + this.labelxs;
38740                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38741                 }
38742             }
38743             
38744             label.cls += ' ' + labelCls;
38745             
38746             cfg.cn.push(label);
38747         }
38748         
38749         Roo.each(['day', 'month', 'year'], function(t){
38750             cfg.cn.push({
38751                 tag : 'div',
38752                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38753             });
38754         }, this);
38755         
38756         return cfg;
38757     },
38758     
38759     inputEl: function ()
38760     {
38761         return this.el.select('.roo-date-split-field-group-value', true).first();
38762     },
38763     
38764     onRender : function(ct, position) 
38765     {
38766         var _this = this;
38767         
38768         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38769         
38770         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38771         
38772         this.dayField = new Roo.bootstrap.form.ComboBox({
38773             allowBlank : this.dayAllowBlank,
38774             alwaysQuery : true,
38775             displayField : 'value',
38776             editable : false,
38777             fieldLabel : '',
38778             forceSelection : true,
38779             mode : 'local',
38780             placeholder : this.dayPlaceholder,
38781             selectOnFocus : true,
38782             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38783             triggerAction : 'all',
38784             typeAhead : true,
38785             valueField : 'value',
38786             store : new Roo.data.SimpleStore({
38787                 data : (function() {    
38788                     var days = [];
38789                     _this.fireEvent('days', _this, days);
38790                     return days;
38791                 })(),
38792                 fields : [ 'value' ]
38793             }),
38794             listeners : {
38795                 select : function (_self, record, index)
38796                 {
38797                     _this.setValue(_this.getValue());
38798                 }
38799             }
38800         });
38801
38802         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38803         
38804         this.monthField = new Roo.bootstrap.form.MonthField({
38805             after : '<i class=\"fa fa-calendar\"></i>',
38806             allowBlank : this.monthAllowBlank,
38807             placeholder : this.monthPlaceholder,
38808             readOnly : true,
38809             listeners : {
38810                 render : function (_self)
38811                 {
38812                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38813                         e.preventDefault();
38814                         _self.focus();
38815                     });
38816                 },
38817                 select : function (_self, oldvalue, newvalue)
38818                 {
38819                     _this.setValue(_this.getValue());
38820                 }
38821             }
38822         });
38823         
38824         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38825         
38826         this.yearField = new Roo.bootstrap.form.ComboBox({
38827             allowBlank : this.yearAllowBlank,
38828             alwaysQuery : true,
38829             displayField : 'value',
38830             editable : false,
38831             fieldLabel : '',
38832             forceSelection : true,
38833             mode : 'local',
38834             placeholder : this.yearPlaceholder,
38835             selectOnFocus : true,
38836             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38837             triggerAction : 'all',
38838             typeAhead : true,
38839             valueField : 'value',
38840             store : new Roo.data.SimpleStore({
38841                 data : (function() {
38842                     var years = [];
38843                     _this.fireEvent('years', _this, years);
38844                     return years;
38845                 })(),
38846                 fields : [ 'value' ]
38847             }),
38848             listeners : {
38849                 select : function (_self, record, index)
38850                 {
38851                     _this.setValue(_this.getValue());
38852                 }
38853             }
38854         });
38855
38856         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38857     },
38858     
38859     setValue : function(v, format)
38860     {
38861         this.inputEl.dom.value = v;
38862         
38863         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38864         
38865         var d = Date.parseDate(v, f);
38866         
38867         if(!d){
38868             this.validate();
38869             return;
38870         }
38871         
38872         this.setDay(d.format(this.dayFormat));
38873         this.setMonth(d.format(this.monthFormat));
38874         this.setYear(d.format(this.yearFormat));
38875         
38876         this.validate();
38877         
38878         return;
38879     },
38880     
38881     setDay : function(v)
38882     {
38883         this.dayField.setValue(v);
38884         this.inputEl.dom.value = this.getValue();
38885         this.validate();
38886         return;
38887     },
38888     
38889     setMonth : function(v)
38890     {
38891         this.monthField.setValue(v, true);
38892         this.inputEl.dom.value = this.getValue();
38893         this.validate();
38894         return;
38895     },
38896     
38897     setYear : function(v)
38898     {
38899         this.yearField.setValue(v);
38900         this.inputEl.dom.value = this.getValue();
38901         this.validate();
38902         return;
38903     },
38904     
38905     getDay : function()
38906     {
38907         return this.dayField.getValue();
38908     },
38909     
38910     getMonth : function()
38911     {
38912         return this.monthField.getValue();
38913     },
38914     
38915     getYear : function()
38916     {
38917         return this.yearField.getValue();
38918     },
38919     
38920     getValue : function()
38921     {
38922         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38923         
38924         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38925         
38926         return date;
38927     },
38928     
38929     reset : function()
38930     {
38931         this.setDay('');
38932         this.setMonth('');
38933         this.setYear('');
38934         this.inputEl.dom.value = '';
38935         this.validate();
38936         return;
38937     },
38938     
38939     validate : function()
38940     {
38941         var d = this.dayField.validate();
38942         var m = this.monthField.validate();
38943         var y = this.yearField.validate();
38944         
38945         var valid = true;
38946         
38947         if(
38948                 (!this.dayAllowBlank && !d) ||
38949                 (!this.monthAllowBlank && !m) ||
38950                 (!this.yearAllowBlank && !y)
38951         ){
38952             valid = false;
38953         }
38954         
38955         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38956             return valid;
38957         }
38958         
38959         if(valid){
38960             this.markValid();
38961             return valid;
38962         }
38963         
38964         this.markInvalid();
38965         
38966         return valid;
38967     },
38968     
38969     markValid : function()
38970     {
38971         
38972         var label = this.el.select('label', true).first();
38973         var icon = this.el.select('i.fa-star', true).first();
38974
38975         if(label && icon){
38976             icon.remove();
38977         }
38978         
38979         this.fireEvent('valid', this);
38980     },
38981     
38982      /**
38983      * Mark this field as invalid
38984      * @param {String} msg The validation message
38985      */
38986     markInvalid : function(msg)
38987     {
38988         
38989         var label = this.el.select('label', true).first();
38990         var icon = this.el.select('i.fa-star', true).first();
38991
38992         if(label && !icon){
38993             this.el.select('.roo-date-split-field-label', true).createChild({
38994                 tag : 'i',
38995                 cls : 'text-danger fa fa-lg fa-star',
38996                 tooltip : 'This field is required',
38997                 style : 'margin-right:5px;'
38998             }, label, true);
38999         }
39000         
39001         this.fireEvent('invalid', this, msg);
39002     },
39003     
39004     clearInvalid : function()
39005     {
39006         var label = this.el.select('label', true).first();
39007         var icon = this.el.select('i.fa-star', true).first();
39008
39009         if(label && icon){
39010             icon.remove();
39011         }
39012         
39013         this.fireEvent('valid', this);
39014     },
39015     
39016     getName: function()
39017     {
39018         return this.name;
39019     }
39020     
39021 });
39022
39023  
39024
39025 /**
39026  * @class Roo.bootstrap.LayoutMasonry
39027  * @extends Roo.bootstrap.Component
39028  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39029  * Bootstrap Layout Masonry class
39030  *
39031  * This is based on 
39032  * http://masonry.desandro.com
39033  *
39034  * The idea is to render all the bricks based on vertical width...
39035  *
39036  * The original code extends 'outlayer' - we might need to use that....
39037
39038  * @constructor
39039  * Create a new Element
39040  * @param {Object} config The config object
39041  */
39042
39043 Roo.bootstrap.LayoutMasonry = function(config){
39044     
39045     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39046     
39047     this.bricks = [];
39048     
39049     Roo.bootstrap.LayoutMasonry.register(this);
39050     
39051     this.addEvents({
39052         // raw events
39053         /**
39054          * @event layout
39055          * Fire after layout the items
39056          * @param {Roo.bootstrap.LayoutMasonry} this
39057          * @param {Roo.EventObject} e
39058          */
39059         "layout" : true
39060     });
39061     
39062 };
39063
39064 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
39065     
39066     /**
39067      * @cfg {Boolean} isLayoutInstant = no animation?
39068      */   
39069     isLayoutInstant : false, // needed?
39070    
39071     /**
39072      * @cfg {Number} boxWidth  width of the columns
39073      */   
39074     boxWidth : 450,
39075     
39076       /**
39077      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
39078      */   
39079     boxHeight : 0,
39080     
39081     /**
39082      * @cfg {Number} padWidth padding below box..
39083      */   
39084     padWidth : 10, 
39085     
39086     /**
39087      * @cfg {Number} gutter gutter width..
39088      */   
39089     gutter : 10,
39090     
39091      /**
39092      * @cfg {Number} maxCols maximum number of columns
39093      */   
39094     
39095     maxCols: 0,
39096     
39097     /**
39098      * @cfg {Boolean} isAutoInitial defalut true
39099      */   
39100     isAutoInitial : true, 
39101     
39102     containerWidth: 0,
39103     
39104     /**
39105      * @cfg {Boolean} isHorizontal defalut false
39106      */   
39107     isHorizontal : false, 
39108
39109     currentSize : null,
39110     
39111     tag: 'div',
39112     
39113     cls: '',
39114     
39115     bricks: null, //CompositeElement
39116     
39117     cols : 1,
39118     
39119     _isLayoutInited : false,
39120     
39121 //    isAlternative : false, // only use for vertical layout...
39122     
39123     /**
39124      * @cfg {Number} alternativePadWidth padding below box..
39125      */   
39126     alternativePadWidth : 50,
39127     
39128     selectedBrick : [],
39129     
39130     getAutoCreate : function(){
39131         
39132         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39133         
39134         var cfg = {
39135             tag: this.tag,
39136             cls: 'blog-masonary-wrapper ' + this.cls,
39137             cn : {
39138                 cls : 'mas-boxes masonary'
39139             }
39140         };
39141         
39142         return cfg;
39143     },
39144     
39145     getChildContainer: function( )
39146     {
39147         if (this.boxesEl) {
39148             return this.boxesEl;
39149         }
39150         
39151         this.boxesEl = this.el.select('.mas-boxes').first();
39152         
39153         return this.boxesEl;
39154     },
39155     
39156     
39157     initEvents : function()
39158     {
39159         var _this = this;
39160         
39161         if(this.isAutoInitial){
39162             Roo.log('hook children rendered');
39163             this.on('childrenrendered', function() {
39164                 Roo.log('children rendered');
39165                 _this.initial();
39166             } ,this);
39167         }
39168     },
39169     
39170     initial : function()
39171     {
39172         this.selectedBrick = [];
39173         
39174         this.currentSize = this.el.getBox(true);
39175         
39176         Roo.EventManager.onWindowResize(this.resize, this); 
39177
39178         if(!this.isAutoInitial){
39179             this.layout();
39180             return;
39181         }
39182         
39183         this.layout();
39184         
39185         return;
39186         //this.layout.defer(500,this);
39187         
39188     },
39189     
39190     resize : function()
39191     {
39192         var cs = this.el.getBox(true);
39193         
39194         if (
39195                 this.currentSize.width == cs.width && 
39196                 this.currentSize.x == cs.x && 
39197                 this.currentSize.height == cs.height && 
39198                 this.currentSize.y == cs.y 
39199         ) {
39200             Roo.log("no change in with or X or Y");
39201             return;
39202         }
39203         
39204         this.currentSize = cs;
39205         
39206         this.layout();
39207         
39208     },
39209     
39210     layout : function()
39211     {   
39212         this._resetLayout();
39213         
39214         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39215         
39216         this.layoutItems( isInstant );
39217       
39218         this._isLayoutInited = true;
39219         
39220         this.fireEvent('layout', this);
39221         
39222     },
39223     
39224     _resetLayout : function()
39225     {
39226         if(this.isHorizontal){
39227             this.horizontalMeasureColumns();
39228             return;
39229         }
39230         
39231         this.verticalMeasureColumns();
39232         
39233     },
39234     
39235     verticalMeasureColumns : function()
39236     {
39237         this.getContainerWidth();
39238         
39239 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39240 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
39241 //            return;
39242 //        }
39243         
39244         var boxWidth = this.boxWidth + this.padWidth;
39245         
39246         if(this.containerWidth < this.boxWidth){
39247             boxWidth = this.containerWidth
39248         }
39249         
39250         var containerWidth = this.containerWidth;
39251         
39252         var cols = Math.floor(containerWidth / boxWidth);
39253         
39254         this.cols = Math.max( cols, 1 );
39255         
39256         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39257         
39258         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39259         
39260         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39261         
39262         this.colWidth = boxWidth + avail - this.padWidth;
39263         
39264         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39265         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
39266     },
39267     
39268     horizontalMeasureColumns : function()
39269     {
39270         this.getContainerWidth();
39271         
39272         var boxWidth = this.boxWidth;
39273         
39274         if(this.containerWidth < boxWidth){
39275             boxWidth = this.containerWidth;
39276         }
39277         
39278         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39279         
39280         this.el.setHeight(boxWidth);
39281         
39282     },
39283     
39284     getContainerWidth : function()
39285     {
39286         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39287     },
39288     
39289     layoutItems : function( isInstant )
39290     {
39291         Roo.log(this.bricks);
39292         
39293         var items = Roo.apply([], this.bricks);
39294         
39295         if(this.isHorizontal){
39296             this._horizontalLayoutItems( items , isInstant );
39297             return;
39298         }
39299         
39300 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39301 //            this._verticalAlternativeLayoutItems( items , isInstant );
39302 //            return;
39303 //        }
39304         
39305         this._verticalLayoutItems( items , isInstant );
39306         
39307     },
39308     
39309     _verticalLayoutItems : function ( items , isInstant)
39310     {
39311         if ( !items || !items.length ) {
39312             return;
39313         }
39314         
39315         var standard = [
39316             ['xs', 'xs', 'xs', 'tall'],
39317             ['xs', 'xs', 'tall'],
39318             ['xs', 'xs', 'sm'],
39319             ['xs', 'xs', 'xs'],
39320             ['xs', 'tall'],
39321             ['xs', 'sm'],
39322             ['xs', 'xs'],
39323             ['xs'],
39324             
39325             ['sm', 'xs', 'xs'],
39326             ['sm', 'xs'],
39327             ['sm'],
39328             
39329             ['tall', 'xs', 'xs', 'xs'],
39330             ['tall', 'xs', 'xs'],
39331             ['tall', 'xs'],
39332             ['tall']
39333             
39334         ];
39335         
39336         var queue = [];
39337         
39338         var boxes = [];
39339         
39340         var box = [];
39341         
39342         Roo.each(items, function(item, k){
39343             
39344             switch (item.size) {
39345                 // these layouts take up a full box,
39346                 case 'md' :
39347                 case 'md-left' :
39348                 case 'md-right' :
39349                 case 'wide' :
39350                     
39351                     if(box.length){
39352                         boxes.push(box);
39353                         box = [];
39354                     }
39355                     
39356                     boxes.push([item]);
39357                     
39358                     break;
39359                     
39360                 case 'xs' :
39361                 case 'sm' :
39362                 case 'tall' :
39363                     
39364                     box.push(item);
39365                     
39366                     break;
39367                 default :
39368                     break;
39369                     
39370             }
39371             
39372         }, this);
39373         
39374         if(box.length){
39375             boxes.push(box);
39376             box = [];
39377         }
39378         
39379         var filterPattern = function(box, length)
39380         {
39381             if(!box.length){
39382                 return;
39383             }
39384             
39385             var match = false;
39386             
39387             var pattern = box.slice(0, length);
39388             
39389             var format = [];
39390             
39391             Roo.each(pattern, function(i){
39392                 format.push(i.size);
39393             }, this);
39394             
39395             Roo.each(standard, function(s){
39396                 
39397                 if(String(s) != String(format)){
39398                     return;
39399                 }
39400                 
39401                 match = true;
39402                 return false;
39403                 
39404             }, this);
39405             
39406             if(!match && length == 1){
39407                 return;
39408             }
39409             
39410             if(!match){
39411                 filterPattern(box, length - 1);
39412                 return;
39413             }
39414                 
39415             queue.push(pattern);
39416
39417             box = box.slice(length, box.length);
39418
39419             filterPattern(box, 4);
39420
39421             return;
39422             
39423         }
39424         
39425         Roo.each(boxes, function(box, k){
39426             
39427             if(!box.length){
39428                 return;
39429             }
39430             
39431             if(box.length == 1){
39432                 queue.push(box);
39433                 return;
39434             }
39435             
39436             filterPattern(box, 4);
39437             
39438         }, this);
39439         
39440         this._processVerticalLayoutQueue( queue, isInstant );
39441         
39442     },
39443     
39444 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39445 //    {
39446 //        if ( !items || !items.length ) {
39447 //            return;
39448 //        }
39449 //
39450 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39451 //        
39452 //    },
39453     
39454     _horizontalLayoutItems : function ( items , isInstant)
39455     {
39456         if ( !items || !items.length || items.length < 3) {
39457             return;
39458         }
39459         
39460         items.reverse();
39461         
39462         var eItems = items.slice(0, 3);
39463         
39464         items = items.slice(3, items.length);
39465         
39466         var standard = [
39467             ['xs', 'xs', 'xs', 'wide'],
39468             ['xs', 'xs', 'wide'],
39469             ['xs', 'xs', 'sm'],
39470             ['xs', 'xs', 'xs'],
39471             ['xs', 'wide'],
39472             ['xs', 'sm'],
39473             ['xs', 'xs'],
39474             ['xs'],
39475             
39476             ['sm', 'xs', 'xs'],
39477             ['sm', 'xs'],
39478             ['sm'],
39479             
39480             ['wide', 'xs', 'xs', 'xs'],
39481             ['wide', 'xs', 'xs'],
39482             ['wide', 'xs'],
39483             ['wide'],
39484             
39485             ['wide-thin']
39486         ];
39487         
39488         var queue = [];
39489         
39490         var boxes = [];
39491         
39492         var box = [];
39493         
39494         Roo.each(items, function(item, k){
39495             
39496             switch (item.size) {
39497                 case 'md' :
39498                 case 'md-left' :
39499                 case 'md-right' :
39500                 case 'tall' :
39501                     
39502                     if(box.length){
39503                         boxes.push(box);
39504                         box = [];
39505                     }
39506                     
39507                     boxes.push([item]);
39508                     
39509                     break;
39510                     
39511                 case 'xs' :
39512                 case 'sm' :
39513                 case 'wide' :
39514                 case 'wide-thin' :
39515                     
39516                     box.push(item);
39517                     
39518                     break;
39519                 default :
39520                     break;
39521                     
39522             }
39523             
39524         }, this);
39525         
39526         if(box.length){
39527             boxes.push(box);
39528             box = [];
39529         }
39530         
39531         var filterPattern = function(box, length)
39532         {
39533             if(!box.length){
39534                 return;
39535             }
39536             
39537             var match = false;
39538             
39539             var pattern = box.slice(0, length);
39540             
39541             var format = [];
39542             
39543             Roo.each(pattern, function(i){
39544                 format.push(i.size);
39545             }, this);
39546             
39547             Roo.each(standard, function(s){
39548                 
39549                 if(String(s) != String(format)){
39550                     return;
39551                 }
39552                 
39553                 match = true;
39554                 return false;
39555                 
39556             }, this);
39557             
39558             if(!match && length == 1){
39559                 return;
39560             }
39561             
39562             if(!match){
39563                 filterPattern(box, length - 1);
39564                 return;
39565             }
39566                 
39567             queue.push(pattern);
39568
39569             box = box.slice(length, box.length);
39570
39571             filterPattern(box, 4);
39572
39573             return;
39574             
39575         }
39576         
39577         Roo.each(boxes, function(box, k){
39578             
39579             if(!box.length){
39580                 return;
39581             }
39582             
39583             if(box.length == 1){
39584                 queue.push(box);
39585                 return;
39586             }
39587             
39588             filterPattern(box, 4);
39589             
39590         }, this);
39591         
39592         
39593         var prune = [];
39594         
39595         var pos = this.el.getBox(true);
39596         
39597         var minX = pos.x;
39598         
39599         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39600         
39601         var hit_end = false;
39602         
39603         Roo.each(queue, function(box){
39604             
39605             if(hit_end){
39606                 
39607                 Roo.each(box, function(b){
39608                 
39609                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39610                     b.el.hide();
39611
39612                 }, this);
39613
39614                 return;
39615             }
39616             
39617             var mx = 0;
39618             
39619             Roo.each(box, function(b){
39620                 
39621                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39622                 b.el.show();
39623
39624                 mx = Math.max(mx, b.x);
39625                 
39626             }, this);
39627             
39628             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39629             
39630             if(maxX < minX){
39631                 
39632                 Roo.each(box, function(b){
39633                 
39634                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39635                     b.el.hide();
39636                     
39637                 }, this);
39638                 
39639                 hit_end = true;
39640                 
39641                 return;
39642             }
39643             
39644             prune.push(box);
39645             
39646         }, this);
39647         
39648         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39649     },
39650     
39651     /** Sets position of item in DOM
39652     * @param {Element} item
39653     * @param {Number} x - horizontal position
39654     * @param {Number} y - vertical position
39655     * @param {Boolean} isInstant - disables transitions
39656     */
39657     _processVerticalLayoutQueue : function( queue, isInstant )
39658     {
39659         var pos = this.el.getBox(true);
39660         var x = pos.x;
39661         var y = pos.y;
39662         var maxY = [];
39663         
39664         for (var i = 0; i < this.cols; i++){
39665             maxY[i] = pos.y;
39666         }
39667         
39668         Roo.each(queue, function(box, k){
39669             
39670             var col = k % this.cols;
39671             
39672             Roo.each(box, function(b,kk){
39673                 
39674                 b.el.position('absolute');
39675                 
39676                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39677                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39678                 
39679                 if(b.size == 'md-left' || b.size == 'md-right'){
39680                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39681                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39682                 }
39683                 
39684                 b.el.setWidth(width);
39685                 b.el.setHeight(height);
39686                 // iframe?
39687                 b.el.select('iframe',true).setSize(width,height);
39688                 
39689             }, this);
39690             
39691             for (var i = 0; i < this.cols; i++){
39692                 
39693                 if(maxY[i] < maxY[col]){
39694                     col = i;
39695                     continue;
39696                 }
39697                 
39698                 col = Math.min(col, i);
39699                 
39700             }
39701             
39702             x = pos.x + col * (this.colWidth + this.padWidth);
39703             
39704             y = maxY[col];
39705             
39706             var positions = [];
39707             
39708             switch (box.length){
39709                 case 1 :
39710                     positions = this.getVerticalOneBoxColPositions(x, y, box);
39711                     break;
39712                 case 2 :
39713                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
39714                     break;
39715                 case 3 :
39716                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
39717                     break;
39718                 case 4 :
39719                     positions = this.getVerticalFourBoxColPositions(x, y, box);
39720                     break;
39721                 default :
39722                     break;
39723             }
39724             
39725             Roo.each(box, function(b,kk){
39726                 
39727                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39728                 
39729                 var sz = b.el.getSize();
39730                 
39731                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39732                 
39733             }, this);
39734             
39735         }, this);
39736         
39737         var mY = 0;
39738         
39739         for (var i = 0; i < this.cols; i++){
39740             mY = Math.max(mY, maxY[i]);
39741         }
39742         
39743         this.el.setHeight(mY - pos.y);
39744         
39745     },
39746     
39747 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39748 //    {
39749 //        var pos = this.el.getBox(true);
39750 //        var x = pos.x;
39751 //        var y = pos.y;
39752 //        var maxX = pos.right;
39753 //        
39754 //        var maxHeight = 0;
39755 //        
39756 //        Roo.each(items, function(item, k){
39757 //            
39758 //            var c = k % 2;
39759 //            
39760 //            item.el.position('absolute');
39761 //                
39762 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39763 //
39764 //            item.el.setWidth(width);
39765 //
39766 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39767 //
39768 //            item.el.setHeight(height);
39769 //            
39770 //            if(c == 0){
39771 //                item.el.setXY([x, y], isInstant ? false : true);
39772 //            } else {
39773 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
39774 //            }
39775 //            
39776 //            y = y + height + this.alternativePadWidth;
39777 //            
39778 //            maxHeight = maxHeight + height + this.alternativePadWidth;
39779 //            
39780 //        }, this);
39781 //        
39782 //        this.el.setHeight(maxHeight);
39783 //        
39784 //    },
39785     
39786     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39787     {
39788         var pos = this.el.getBox(true);
39789         
39790         var minX = pos.x;
39791         var minY = pos.y;
39792         
39793         var maxX = pos.right;
39794         
39795         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39796         
39797         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39798         
39799         Roo.each(queue, function(box, k){
39800             
39801             Roo.each(box, function(b, kk){
39802                 
39803                 b.el.position('absolute');
39804                 
39805                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39806                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39807                 
39808                 if(b.size == 'md-left' || b.size == 'md-right'){
39809                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39810                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39811                 }
39812                 
39813                 b.el.setWidth(width);
39814                 b.el.setHeight(height);
39815                 
39816             }, this);
39817             
39818             if(!box.length){
39819                 return;
39820             }
39821             
39822             var positions = [];
39823             
39824             switch (box.length){
39825                 case 1 :
39826                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39827                     break;
39828                 case 2 :
39829                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39830                     break;
39831                 case 3 :
39832                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39833                     break;
39834                 case 4 :
39835                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39836                     break;
39837                 default :
39838                     break;
39839             }
39840             
39841             Roo.each(box, function(b,kk){
39842                 
39843                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39844                 
39845                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39846                 
39847             }, this);
39848             
39849         }, this);
39850         
39851     },
39852     
39853     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39854     {
39855         Roo.each(eItems, function(b,k){
39856             
39857             b.size = (k == 0) ? 'sm' : 'xs';
39858             b.x = (k == 0) ? 2 : 1;
39859             b.y = (k == 0) ? 2 : 1;
39860             
39861             b.el.position('absolute');
39862             
39863             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39864                 
39865             b.el.setWidth(width);
39866             
39867             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39868             
39869             b.el.setHeight(height);
39870             
39871         }, this);
39872
39873         var positions = [];
39874         
39875         positions.push({
39876             x : maxX - this.unitWidth * 2 - this.gutter,
39877             y : minY
39878         });
39879         
39880         positions.push({
39881             x : maxX - this.unitWidth,
39882             y : minY + (this.unitWidth + this.gutter) * 2
39883         });
39884         
39885         positions.push({
39886             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39887             y : minY
39888         });
39889         
39890         Roo.each(eItems, function(b,k){
39891             
39892             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39893
39894         }, this);
39895         
39896     },
39897     
39898     getVerticalOneBoxColPositions : function(x, y, box)
39899     {
39900         var pos = [];
39901         
39902         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39903         
39904         if(box[0].size == 'md-left'){
39905             rand = 0;
39906         }
39907         
39908         if(box[0].size == 'md-right'){
39909             rand = 1;
39910         }
39911         
39912         pos.push({
39913             x : x + (this.unitWidth + this.gutter) * rand,
39914             y : y
39915         });
39916         
39917         return pos;
39918     },
39919     
39920     getVerticalTwoBoxColPositions : function(x, y, box)
39921     {
39922         var pos = [];
39923         
39924         if(box[0].size == 'xs'){
39925             
39926             pos.push({
39927                 x : x,
39928                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39929             });
39930
39931             pos.push({
39932                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39933                 y : y
39934             });
39935             
39936             return pos;
39937             
39938         }
39939         
39940         pos.push({
39941             x : x,
39942             y : y
39943         });
39944
39945         pos.push({
39946             x : x + (this.unitWidth + this.gutter) * 2,
39947             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39948         });
39949         
39950         return pos;
39951         
39952     },
39953     
39954     getVerticalThreeBoxColPositions : function(x, y, box)
39955     {
39956         var pos = [];
39957         
39958         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39959             
39960             pos.push({
39961                 x : x,
39962                 y : y
39963             });
39964
39965             pos.push({
39966                 x : x + (this.unitWidth + this.gutter) * 1,
39967                 y : y
39968             });
39969             
39970             pos.push({
39971                 x : x + (this.unitWidth + this.gutter) * 2,
39972                 y : y
39973             });
39974             
39975             return pos;
39976             
39977         }
39978         
39979         if(box[0].size == 'xs' && box[1].size == 'xs'){
39980             
39981             pos.push({
39982                 x : x,
39983                 y : y
39984             });
39985
39986             pos.push({
39987                 x : x,
39988                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39989             });
39990             
39991             pos.push({
39992                 x : x + (this.unitWidth + this.gutter) * 1,
39993                 y : y
39994             });
39995             
39996             return pos;
39997             
39998         }
39999         
40000         pos.push({
40001             x : x,
40002             y : y
40003         });
40004
40005         pos.push({
40006             x : x + (this.unitWidth + this.gutter) * 2,
40007             y : y
40008         });
40009
40010         pos.push({
40011             x : x + (this.unitWidth + this.gutter) * 2,
40012             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40013         });
40014             
40015         return pos;
40016         
40017     },
40018     
40019     getVerticalFourBoxColPositions : function(x, y, box)
40020     {
40021         var pos = [];
40022         
40023         if(box[0].size == 'xs'){
40024             
40025             pos.push({
40026                 x : x,
40027                 y : y
40028             });
40029
40030             pos.push({
40031                 x : x,
40032                 y : y + (this.unitHeight + this.gutter) * 1
40033             });
40034             
40035             pos.push({
40036                 x : x,
40037                 y : y + (this.unitHeight + this.gutter) * 2
40038             });
40039             
40040             pos.push({
40041                 x : x + (this.unitWidth + this.gutter) * 1,
40042                 y : y
40043             });
40044             
40045             return pos;
40046             
40047         }
40048         
40049         pos.push({
40050             x : x,
40051             y : y
40052         });
40053
40054         pos.push({
40055             x : x + (this.unitWidth + this.gutter) * 2,
40056             y : y
40057         });
40058
40059         pos.push({
40060             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40061             y : y + (this.unitHeight + this.gutter) * 1
40062         });
40063
40064         pos.push({
40065             x : x + (this.unitWidth + this.gutter) * 2,
40066             y : y + (this.unitWidth + this.gutter) * 2
40067         });
40068
40069         return pos;
40070         
40071     },
40072     
40073     getHorizontalOneBoxColPositions : function(maxX, minY, box)
40074     {
40075         var pos = [];
40076         
40077         if(box[0].size == 'md-left'){
40078             pos.push({
40079                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40080                 y : minY
40081             });
40082             
40083             return pos;
40084         }
40085         
40086         if(box[0].size == 'md-right'){
40087             pos.push({
40088                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40089                 y : minY + (this.unitWidth + this.gutter) * 1
40090             });
40091             
40092             return pos;
40093         }
40094         
40095         var rand = Math.floor(Math.random() * (4 - box[0].y));
40096         
40097         pos.push({
40098             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40099             y : minY + (this.unitWidth + this.gutter) * rand
40100         });
40101         
40102         return pos;
40103         
40104     },
40105     
40106     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40107     {
40108         var pos = [];
40109         
40110         if(box[0].size == 'xs'){
40111             
40112             pos.push({
40113                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40114                 y : minY
40115             });
40116
40117             pos.push({
40118                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40119                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40120             });
40121             
40122             return pos;
40123             
40124         }
40125         
40126         pos.push({
40127             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40128             y : minY
40129         });
40130
40131         pos.push({
40132             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40133             y : minY + (this.unitWidth + this.gutter) * 2
40134         });
40135         
40136         return pos;
40137         
40138     },
40139     
40140     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40141     {
40142         var pos = [];
40143         
40144         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40145             
40146             pos.push({
40147                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40148                 y : minY
40149             });
40150
40151             pos.push({
40152                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40153                 y : minY + (this.unitWidth + this.gutter) * 1
40154             });
40155             
40156             pos.push({
40157                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40158                 y : minY + (this.unitWidth + this.gutter) * 2
40159             });
40160             
40161             return pos;
40162             
40163         }
40164         
40165         if(box[0].size == 'xs' && box[1].size == 'xs'){
40166             
40167             pos.push({
40168                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40169                 y : minY
40170             });
40171
40172             pos.push({
40173                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40174                 y : minY
40175             });
40176             
40177             pos.push({
40178                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40179                 y : minY + (this.unitWidth + this.gutter) * 1
40180             });
40181             
40182             return pos;
40183             
40184         }
40185         
40186         pos.push({
40187             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40188             y : minY
40189         });
40190
40191         pos.push({
40192             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40193             y : minY + (this.unitWidth + this.gutter) * 2
40194         });
40195
40196         pos.push({
40197             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40198             y : minY + (this.unitWidth + this.gutter) * 2
40199         });
40200             
40201         return pos;
40202         
40203     },
40204     
40205     getHorizontalFourBoxColPositions : function(maxX, minY, box)
40206     {
40207         var pos = [];
40208         
40209         if(box[0].size == 'xs'){
40210             
40211             pos.push({
40212                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40213                 y : minY
40214             });
40215
40216             pos.push({
40217                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40218                 y : minY
40219             });
40220             
40221             pos.push({
40222                 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),
40223                 y : minY
40224             });
40225             
40226             pos.push({
40227                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40228                 y : minY + (this.unitWidth + this.gutter) * 1
40229             });
40230             
40231             return pos;
40232             
40233         }
40234         
40235         pos.push({
40236             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40237             y : minY
40238         });
40239         
40240         pos.push({
40241             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40242             y : minY + (this.unitWidth + this.gutter) * 2
40243         });
40244         
40245         pos.push({
40246             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40247             y : minY + (this.unitWidth + this.gutter) * 2
40248         });
40249         
40250         pos.push({
40251             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),
40252             y : minY + (this.unitWidth + this.gutter) * 2
40253         });
40254
40255         return pos;
40256         
40257     },
40258     
40259     /**
40260     * remove a Masonry Brick
40261     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40262     */
40263     removeBrick : function(brick_id)
40264     {
40265         if (!brick_id) {
40266             return;
40267         }
40268         
40269         for (var i = 0; i<this.bricks.length; i++) {
40270             if (this.bricks[i].id == brick_id) {
40271                 this.bricks.splice(i,1);
40272                 this.el.dom.removeChild(Roo.get(brick_id).dom);
40273                 this.initial();
40274             }
40275         }
40276     },
40277     
40278     /**
40279     * adds a Masonry Brick
40280     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40281     */
40282     addBrick : function(cfg)
40283     {
40284         var cn = new Roo.bootstrap.MasonryBrick(cfg);
40285         //this.register(cn);
40286         cn.parentId = this.id;
40287         cn.render(this.el);
40288         return cn;
40289     },
40290     
40291     /**
40292     * register a Masonry Brick
40293     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40294     */
40295     
40296     register : function(brick)
40297     {
40298         this.bricks.push(brick);
40299         brick.masonryId = this.id;
40300     },
40301     
40302     /**
40303     * clear all the Masonry Brick
40304     */
40305     clearAll : function()
40306     {
40307         this.bricks = [];
40308         //this.getChildContainer().dom.innerHTML = "";
40309         this.el.dom.innerHTML = '';
40310     },
40311     
40312     getSelected : function()
40313     {
40314         if (!this.selectedBrick) {
40315             return false;
40316         }
40317         
40318         return this.selectedBrick;
40319     }
40320 });
40321
40322 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40323     
40324     groups: {},
40325      /**
40326     * register a Masonry Layout
40327     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40328     */
40329     
40330     register : function(layout)
40331     {
40332         this.groups[layout.id] = layout;
40333     },
40334     /**
40335     * fetch a  Masonry Layout based on the masonry layout ID
40336     * @param {string} the masonry layout to add
40337     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40338     */
40339     
40340     get: function(layout_id) {
40341         if (typeof(this.groups[layout_id]) == 'undefined') {
40342             return false;
40343         }
40344         return this.groups[layout_id] ;
40345     }
40346     
40347     
40348     
40349 });
40350
40351  
40352
40353  /**
40354  *
40355  * This is based on 
40356  * http://masonry.desandro.com
40357  *
40358  * The idea is to render all the bricks based on vertical width...
40359  *
40360  * The original code extends 'outlayer' - we might need to use that....
40361  * 
40362  */
40363
40364
40365 /**
40366  * @class Roo.bootstrap.LayoutMasonryAuto
40367  * @extends Roo.bootstrap.Component
40368  * Bootstrap Layout Masonry class
40369  * 
40370  * @constructor
40371  * Create a new Element
40372  * @param {Object} config The config object
40373  */
40374
40375 Roo.bootstrap.LayoutMasonryAuto = function(config){
40376     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40377 };
40378
40379 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40380     
40381       /**
40382      * @cfg {Boolean} isFitWidth  - resize the width..
40383      */   
40384     isFitWidth : false,  // options..
40385     /**
40386      * @cfg {Boolean} isOriginLeft = left align?
40387      */   
40388     isOriginLeft : true,
40389     /**
40390      * @cfg {Boolean} isOriginTop = top align?
40391      */   
40392     isOriginTop : false,
40393     /**
40394      * @cfg {Boolean} isLayoutInstant = no animation?
40395      */   
40396     isLayoutInstant : false, // needed?
40397     /**
40398      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40399      */   
40400     isResizingContainer : true,
40401     /**
40402      * @cfg {Number} columnWidth  width of the columns 
40403      */   
40404     
40405     columnWidth : 0,
40406     
40407     /**
40408      * @cfg {Number} maxCols maximum number of columns
40409      */   
40410     
40411     maxCols: 0,
40412     /**
40413      * @cfg {Number} padHeight padding below box..
40414      */   
40415     
40416     padHeight : 10, 
40417     
40418     /**
40419      * @cfg {Boolean} isAutoInitial defalut true
40420      */   
40421     
40422     isAutoInitial : true, 
40423     
40424     // private?
40425     gutter : 0,
40426     
40427     containerWidth: 0,
40428     initialColumnWidth : 0,
40429     currentSize : null,
40430     
40431     colYs : null, // array.
40432     maxY : 0,
40433     padWidth: 10,
40434     
40435     
40436     tag: 'div',
40437     cls: '',
40438     bricks: null, //CompositeElement
40439     cols : 0, // array?
40440     // element : null, // wrapped now this.el
40441     _isLayoutInited : null, 
40442     
40443     
40444     getAutoCreate : function(){
40445         
40446         var cfg = {
40447             tag: this.tag,
40448             cls: 'blog-masonary-wrapper ' + this.cls,
40449             cn : {
40450                 cls : 'mas-boxes masonary'
40451             }
40452         };
40453         
40454         return cfg;
40455     },
40456     
40457     getChildContainer: function( )
40458     {
40459         if (this.boxesEl) {
40460             return this.boxesEl;
40461         }
40462         
40463         this.boxesEl = this.el.select('.mas-boxes').first();
40464         
40465         return this.boxesEl;
40466     },
40467     
40468     
40469     initEvents : function()
40470     {
40471         var _this = this;
40472         
40473         if(this.isAutoInitial){
40474             Roo.log('hook children rendered');
40475             this.on('childrenrendered', function() {
40476                 Roo.log('children rendered');
40477                 _this.initial();
40478             } ,this);
40479         }
40480         
40481     },
40482     
40483     initial : function()
40484     {
40485         this.reloadItems();
40486
40487         this.currentSize = this.el.getBox(true);
40488
40489         /// was window resize... - let's see if this works..
40490         Roo.EventManager.onWindowResize(this.resize, this); 
40491
40492         if(!this.isAutoInitial){
40493             this.layout();
40494             return;
40495         }
40496         
40497         this.layout.defer(500,this);
40498     },
40499     
40500     reloadItems: function()
40501     {
40502         this.bricks = this.el.select('.masonry-brick', true);
40503         
40504         this.bricks.each(function(b) {
40505             //Roo.log(b.getSize());
40506             if (!b.attr('originalwidth')) {
40507                 b.attr('originalwidth',  b.getSize().width);
40508             }
40509             
40510         });
40511         
40512         Roo.log(this.bricks.elements.length);
40513     },
40514     
40515     resize : function()
40516     {
40517         Roo.log('resize');
40518         var cs = this.el.getBox(true);
40519         
40520         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40521             Roo.log("no change in with or X");
40522             return;
40523         }
40524         this.currentSize = cs;
40525         this.layout();
40526     },
40527     
40528     layout : function()
40529     {
40530          Roo.log('layout');
40531         this._resetLayout();
40532         //this._manageStamps();
40533       
40534         // don't animate first layout
40535         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40536         this.layoutItems( isInstant );
40537       
40538         // flag for initalized
40539         this._isLayoutInited = true;
40540     },
40541     
40542     layoutItems : function( isInstant )
40543     {
40544         //var items = this._getItemsForLayout( this.items );
40545         // original code supports filtering layout items.. we just ignore it..
40546         
40547         this._layoutItems( this.bricks , isInstant );
40548       
40549         this._postLayout();
40550     },
40551     _layoutItems : function ( items , isInstant)
40552     {
40553        //this.fireEvent( 'layout', this, items );
40554     
40555
40556         if ( !items || !items.elements.length ) {
40557           // no items, emit event with empty array
40558             return;
40559         }
40560
40561         var queue = [];
40562         items.each(function(item) {
40563             Roo.log("layout item");
40564             Roo.log(item);
40565             // get x/y object from method
40566             var position = this._getItemLayoutPosition( item );
40567             // enqueue
40568             position.item = item;
40569             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40570             queue.push( position );
40571         }, this);
40572       
40573         this._processLayoutQueue( queue );
40574     },
40575     /** Sets position of item in DOM
40576     * @param {Element} item
40577     * @param {Number} x - horizontal position
40578     * @param {Number} y - vertical position
40579     * @param {Boolean} isInstant - disables transitions
40580     */
40581     _processLayoutQueue : function( queue )
40582     {
40583         for ( var i=0, len = queue.length; i < len; i++ ) {
40584             var obj = queue[i];
40585             obj.item.position('absolute');
40586             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40587         }
40588     },
40589       
40590     
40591     /**
40592     * Any logic you want to do after each layout,
40593     * i.e. size the container
40594     */
40595     _postLayout : function()
40596     {
40597         this.resizeContainer();
40598     },
40599     
40600     resizeContainer : function()
40601     {
40602         if ( !this.isResizingContainer ) {
40603             return;
40604         }
40605         var size = this._getContainerSize();
40606         if ( size ) {
40607             this.el.setSize(size.width,size.height);
40608             this.boxesEl.setSize(size.width,size.height);
40609         }
40610     },
40611     
40612     
40613     
40614     _resetLayout : function()
40615     {
40616         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40617         this.colWidth = this.el.getWidth();
40618         //this.gutter = this.el.getWidth(); 
40619         
40620         this.measureColumns();
40621
40622         // reset column Y
40623         var i = this.cols;
40624         this.colYs = [];
40625         while (i--) {
40626             this.colYs.push( 0 );
40627         }
40628     
40629         this.maxY = 0;
40630     },
40631
40632     measureColumns : function()
40633     {
40634         this.getContainerWidth();
40635       // if columnWidth is 0, default to outerWidth of first item
40636         if ( !this.columnWidth ) {
40637             var firstItem = this.bricks.first();
40638             Roo.log(firstItem);
40639             this.columnWidth  = this.containerWidth;
40640             if (firstItem && firstItem.attr('originalwidth') ) {
40641                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40642             }
40643             // columnWidth fall back to item of first element
40644             Roo.log("set column width?");
40645                         this.initialColumnWidth = this.columnWidth  ;
40646
40647             // if first elem has no width, default to size of container
40648             
40649         }
40650         
40651         
40652         if (this.initialColumnWidth) {
40653             this.columnWidth = this.initialColumnWidth;
40654         }
40655         
40656         
40657             
40658         // column width is fixed at the top - however if container width get's smaller we should
40659         // reduce it...
40660         
40661         // this bit calcs how man columns..
40662             
40663         var columnWidth = this.columnWidth += this.gutter;
40664       
40665         // calculate columns
40666         var containerWidth = this.containerWidth + this.gutter;
40667         
40668         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40669         // fix rounding errors, typically with gutters
40670         var excess = columnWidth - containerWidth % columnWidth;
40671         
40672         
40673         // if overshoot is less than a pixel, round up, otherwise floor it
40674         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40675         cols = Math[ mathMethod ]( cols );
40676         this.cols = Math.max( cols, 1 );
40677         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40678         
40679          // padding positioning..
40680         var totalColWidth = this.cols * this.columnWidth;
40681         var padavail = this.containerWidth - totalColWidth;
40682         // so for 2 columns - we need 3 'pads'
40683         
40684         var padNeeded = (1+this.cols) * this.padWidth;
40685         
40686         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40687         
40688         this.columnWidth += padExtra
40689         //this.padWidth = Math.floor(padavail /  ( this.cols));
40690         
40691         // adjust colum width so that padding is fixed??
40692         
40693         // we have 3 columns ... total = width * 3
40694         // we have X left over... that should be used by 
40695         
40696         //if (this.expandC) {
40697             
40698         //}
40699         
40700         
40701         
40702     },
40703     
40704     getContainerWidth : function()
40705     {
40706        /* // container is parent if fit width
40707         var container = this.isFitWidth ? this.element.parentNode : this.element;
40708         // check that this.size and size are there
40709         // IE8 triggers resize on body size change, so they might not be
40710         
40711         var size = getSize( container );  //FIXME
40712         this.containerWidth = size && size.innerWidth; //FIXME
40713         */
40714          
40715         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
40716         
40717     },
40718     
40719     _getItemLayoutPosition : function( item )  // what is item?
40720     {
40721         // we resize the item to our columnWidth..
40722       
40723         item.setWidth(this.columnWidth);
40724         item.autoBoxAdjust  = false;
40725         
40726         var sz = item.getSize();
40727  
40728         // how many columns does this brick span
40729         var remainder = this.containerWidth % this.columnWidth;
40730         
40731         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40732         // round if off by 1 pixel, otherwise use ceil
40733         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
40734         colSpan = Math.min( colSpan, this.cols );
40735         
40736         // normally this should be '1' as we dont' currently allow multi width columns..
40737         
40738         var colGroup = this._getColGroup( colSpan );
40739         // get the minimum Y value from the columns
40740         var minimumY = Math.min.apply( Math, colGroup );
40741         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40742         
40743         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
40744          
40745         // position the brick
40746         var position = {
40747             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40748             y: this.currentSize.y + minimumY + this.padHeight
40749         };
40750         
40751         Roo.log(position);
40752         // apply setHeight to necessary columns
40753         var setHeight = minimumY + sz.height + this.padHeight;
40754         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40755         
40756         var setSpan = this.cols + 1 - colGroup.length;
40757         for ( var i = 0; i < setSpan; i++ ) {
40758           this.colYs[ shortColIndex + i ] = setHeight ;
40759         }
40760       
40761         return position;
40762     },
40763     
40764     /**
40765      * @param {Number} colSpan - number of columns the element spans
40766      * @returns {Array} colGroup
40767      */
40768     _getColGroup : function( colSpan )
40769     {
40770         if ( colSpan < 2 ) {
40771           // if brick spans only one column, use all the column Ys
40772           return this.colYs;
40773         }
40774       
40775         var colGroup = [];
40776         // how many different places could this brick fit horizontally
40777         var groupCount = this.cols + 1 - colSpan;
40778         // for each group potential horizontal position
40779         for ( var i = 0; i < groupCount; i++ ) {
40780           // make an array of colY values for that one group
40781           var groupColYs = this.colYs.slice( i, i + colSpan );
40782           // and get the max value of the array
40783           colGroup[i] = Math.max.apply( Math, groupColYs );
40784         }
40785         return colGroup;
40786     },
40787     /*
40788     _manageStamp : function( stamp )
40789     {
40790         var stampSize =  stamp.getSize();
40791         var offset = stamp.getBox();
40792         // get the columns that this stamp affects
40793         var firstX = this.isOriginLeft ? offset.x : offset.right;
40794         var lastX = firstX + stampSize.width;
40795         var firstCol = Math.floor( firstX / this.columnWidth );
40796         firstCol = Math.max( 0, firstCol );
40797         
40798         var lastCol = Math.floor( lastX / this.columnWidth );
40799         // lastCol should not go over if multiple of columnWidth #425
40800         lastCol -= lastX % this.columnWidth ? 0 : 1;
40801         lastCol = Math.min( this.cols - 1, lastCol );
40802         
40803         // set colYs to bottom of the stamp
40804         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40805             stampSize.height;
40806             
40807         for ( var i = firstCol; i <= lastCol; i++ ) {
40808           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40809         }
40810     },
40811     */
40812     
40813     _getContainerSize : function()
40814     {
40815         this.maxY = Math.max.apply( Math, this.colYs );
40816         var size = {
40817             height: this.maxY
40818         };
40819       
40820         if ( this.isFitWidth ) {
40821             size.width = this._getContainerFitWidth();
40822         }
40823       
40824         return size;
40825     },
40826     
40827     _getContainerFitWidth : function()
40828     {
40829         var unusedCols = 0;
40830         // count unused columns
40831         var i = this.cols;
40832         while ( --i ) {
40833           if ( this.colYs[i] !== 0 ) {
40834             break;
40835           }
40836           unusedCols++;
40837         }
40838         // fit container to columns that have been used
40839         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40840     },
40841     
40842     needsResizeLayout : function()
40843     {
40844         var previousWidth = this.containerWidth;
40845         this.getContainerWidth();
40846         return previousWidth !== this.containerWidth;
40847     }
40848  
40849 });
40850
40851  
40852
40853  /*
40854  * - LGPL
40855  *
40856  * element
40857  * 
40858  */
40859
40860 /**
40861  * @class Roo.bootstrap.MasonryBrick
40862  * @extends Roo.bootstrap.Component
40863  * Bootstrap MasonryBrick class
40864  * 
40865  * @constructor
40866  * Create a new MasonryBrick
40867  * @param {Object} config The config object
40868  */
40869
40870 Roo.bootstrap.MasonryBrick = function(config){
40871     
40872     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40873     
40874     Roo.bootstrap.MasonryBrick.register(this);
40875     
40876     this.addEvents({
40877         // raw events
40878         /**
40879          * @event click
40880          * When a MasonryBrick is clcik
40881          * @param {Roo.bootstrap.MasonryBrick} this
40882          * @param {Roo.EventObject} e
40883          */
40884         "click" : true
40885     });
40886 };
40887
40888 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40889     
40890     /**
40891      * @cfg {String} title
40892      */   
40893     title : '',
40894     /**
40895      * @cfg {String} html
40896      */   
40897     html : '',
40898     /**
40899      * @cfg {String} bgimage
40900      */   
40901     bgimage : '',
40902     /**
40903      * @cfg {String} videourl
40904      */   
40905     videourl : '',
40906     /**
40907      * @cfg {String} cls
40908      */   
40909     cls : '',
40910     /**
40911      * @cfg {String} href
40912      */   
40913     href : '',
40914     /**
40915      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40916      */   
40917     size : 'xs',
40918     
40919     /**
40920      * @cfg {String} placetitle (center|bottom)
40921      */   
40922     placetitle : '',
40923     
40924     /**
40925      * @cfg {Boolean} isFitContainer defalut true
40926      */   
40927     isFitContainer : true, 
40928     
40929     /**
40930      * @cfg {Boolean} preventDefault defalut false
40931      */   
40932     preventDefault : false, 
40933     
40934     /**
40935      * @cfg {Boolean} inverse defalut false
40936      */   
40937     maskInverse : false, 
40938     
40939     getAutoCreate : function()
40940     {
40941         if(!this.isFitContainer){
40942             return this.getSplitAutoCreate();
40943         }
40944         
40945         var cls = 'masonry-brick masonry-brick-full';
40946         
40947         if(this.href.length){
40948             cls += ' masonry-brick-link';
40949         }
40950         
40951         if(this.bgimage.length){
40952             cls += ' masonry-brick-image';
40953         }
40954         
40955         if(this.maskInverse){
40956             cls += ' mask-inverse';
40957         }
40958         
40959         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40960             cls += ' enable-mask';
40961         }
40962         
40963         if(this.size){
40964             cls += ' masonry-' + this.size + '-brick';
40965         }
40966         
40967         if(this.placetitle.length){
40968             
40969             switch (this.placetitle) {
40970                 case 'center' :
40971                     cls += ' masonry-center-title';
40972                     break;
40973                 case 'bottom' :
40974                     cls += ' masonry-bottom-title';
40975                     break;
40976                 default:
40977                     break;
40978             }
40979             
40980         } else {
40981             if(!this.html.length && !this.bgimage.length){
40982                 cls += ' masonry-center-title';
40983             }
40984
40985             if(!this.html.length && this.bgimage.length){
40986                 cls += ' masonry-bottom-title';
40987             }
40988         }
40989         
40990         if(this.cls){
40991             cls += ' ' + this.cls;
40992         }
40993         
40994         var cfg = {
40995             tag: (this.href.length) ? 'a' : 'div',
40996             cls: cls,
40997             cn: [
40998                 {
40999                     tag: 'div',
41000                     cls: 'masonry-brick-mask'
41001                 },
41002                 {
41003                     tag: 'div',
41004                     cls: 'masonry-brick-paragraph',
41005                     cn: []
41006                 }
41007             ]
41008         };
41009         
41010         if(this.href.length){
41011             cfg.href = this.href;
41012         }
41013         
41014         var cn = cfg.cn[1].cn;
41015         
41016         if(this.title.length){
41017             cn.push({
41018                 tag: 'h4',
41019                 cls: 'masonry-brick-title',
41020                 html: this.title
41021             });
41022         }
41023         
41024         if(this.html.length){
41025             cn.push({
41026                 tag: 'p',
41027                 cls: 'masonry-brick-text',
41028                 html: this.html
41029             });
41030         }
41031         
41032         if (!this.title.length && !this.html.length) {
41033             cfg.cn[1].cls += ' hide';
41034         }
41035         
41036         if(this.bgimage.length){
41037             cfg.cn.push({
41038                 tag: 'img',
41039                 cls: 'masonry-brick-image-view',
41040                 src: this.bgimage
41041             });
41042         }
41043         
41044         if(this.videourl.length){
41045             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41046             // youtube support only?
41047             cfg.cn.push({
41048                 tag: 'iframe',
41049                 cls: 'masonry-brick-image-view',
41050                 src: vurl,
41051                 frameborder : 0,
41052                 allowfullscreen : true
41053             });
41054         }
41055         
41056         return cfg;
41057         
41058     },
41059     
41060     getSplitAutoCreate : function()
41061     {
41062         var cls = 'masonry-brick masonry-brick-split';
41063         
41064         if(this.href.length){
41065             cls += ' masonry-brick-link';
41066         }
41067         
41068         if(this.bgimage.length){
41069             cls += ' masonry-brick-image';
41070         }
41071         
41072         if(this.size){
41073             cls += ' masonry-' + this.size + '-brick';
41074         }
41075         
41076         switch (this.placetitle) {
41077             case 'center' :
41078                 cls += ' masonry-center-title';
41079                 break;
41080             case 'bottom' :
41081                 cls += ' masonry-bottom-title';
41082                 break;
41083             default:
41084                 if(!this.bgimage.length){
41085                     cls += ' masonry-center-title';
41086                 }
41087
41088                 if(this.bgimage.length){
41089                     cls += ' masonry-bottom-title';
41090                 }
41091                 break;
41092         }
41093         
41094         if(this.cls){
41095             cls += ' ' + this.cls;
41096         }
41097         
41098         var cfg = {
41099             tag: (this.href.length) ? 'a' : 'div',
41100             cls: cls,
41101             cn: [
41102                 {
41103                     tag: 'div',
41104                     cls: 'masonry-brick-split-head',
41105                     cn: [
41106                         {
41107                             tag: 'div',
41108                             cls: 'masonry-brick-paragraph',
41109                             cn: []
41110                         }
41111                     ]
41112                 },
41113                 {
41114                     tag: 'div',
41115                     cls: 'masonry-brick-split-body',
41116                     cn: []
41117                 }
41118             ]
41119         };
41120         
41121         if(this.href.length){
41122             cfg.href = this.href;
41123         }
41124         
41125         if(this.title.length){
41126             cfg.cn[0].cn[0].cn.push({
41127                 tag: 'h4',
41128                 cls: 'masonry-brick-title',
41129                 html: this.title
41130             });
41131         }
41132         
41133         if(this.html.length){
41134             cfg.cn[1].cn.push({
41135                 tag: 'p',
41136                 cls: 'masonry-brick-text',
41137                 html: this.html
41138             });
41139         }
41140
41141         if(this.bgimage.length){
41142             cfg.cn[0].cn.push({
41143                 tag: 'img',
41144                 cls: 'masonry-brick-image-view',
41145                 src: this.bgimage
41146             });
41147         }
41148         
41149         if(this.videourl.length){
41150             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41151             // youtube support only?
41152             cfg.cn[0].cn.cn.push({
41153                 tag: 'iframe',
41154                 cls: 'masonry-brick-image-view',
41155                 src: vurl,
41156                 frameborder : 0,
41157                 allowfullscreen : true
41158             });
41159         }
41160         
41161         return cfg;
41162     },
41163     
41164     initEvents: function() 
41165     {
41166         switch (this.size) {
41167             case 'xs' :
41168                 this.x = 1;
41169                 this.y = 1;
41170                 break;
41171             case 'sm' :
41172                 this.x = 2;
41173                 this.y = 2;
41174                 break;
41175             case 'md' :
41176             case 'md-left' :
41177             case 'md-right' :
41178                 this.x = 3;
41179                 this.y = 3;
41180                 break;
41181             case 'tall' :
41182                 this.x = 2;
41183                 this.y = 3;
41184                 break;
41185             case 'wide' :
41186                 this.x = 3;
41187                 this.y = 2;
41188                 break;
41189             case 'wide-thin' :
41190                 this.x = 3;
41191                 this.y = 1;
41192                 break;
41193                         
41194             default :
41195                 break;
41196         }
41197         
41198         if(Roo.isTouch){
41199             this.el.on('touchstart', this.onTouchStart, this);
41200             this.el.on('touchmove', this.onTouchMove, this);
41201             this.el.on('touchend', this.onTouchEnd, this);
41202             this.el.on('contextmenu', this.onContextMenu, this);
41203         } else {
41204             this.el.on('mouseenter'  ,this.enter, this);
41205             this.el.on('mouseleave', this.leave, this);
41206             this.el.on('click', this.onClick, this);
41207         }
41208         
41209         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41210             this.parent().bricks.push(this);   
41211         }
41212         
41213     },
41214     
41215     onClick: function(e, el)
41216     {
41217         var time = this.endTimer - this.startTimer;
41218         // Roo.log(e.preventDefault());
41219         if(Roo.isTouch){
41220             if(time > 1000){
41221                 e.preventDefault();
41222                 return;
41223             }
41224         }
41225         
41226         if(!this.preventDefault){
41227             return;
41228         }
41229         
41230         e.preventDefault();
41231         
41232         if (this.activeClass != '') {
41233             this.selectBrick();
41234         }
41235         
41236         this.fireEvent('click', this, e);
41237     },
41238     
41239     enter: function(e, el)
41240     {
41241         e.preventDefault();
41242         
41243         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41244             return;
41245         }
41246         
41247         if(this.bgimage.length && this.html.length){
41248             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41249         }
41250     },
41251     
41252     leave: function(e, el)
41253     {
41254         e.preventDefault();
41255         
41256         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
41257             return;
41258         }
41259         
41260         if(this.bgimage.length && this.html.length){
41261             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41262         }
41263     },
41264     
41265     onTouchStart: function(e, el)
41266     {
41267 //        e.preventDefault();
41268         
41269         this.touchmoved = false;
41270         
41271         if(!this.isFitContainer){
41272             return;
41273         }
41274         
41275         if(!this.bgimage.length || !this.html.length){
41276             return;
41277         }
41278         
41279         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41280         
41281         this.timer = new Date().getTime();
41282         
41283     },
41284     
41285     onTouchMove: function(e, el)
41286     {
41287         this.touchmoved = true;
41288     },
41289     
41290     onContextMenu : function(e,el)
41291     {
41292         e.preventDefault();
41293         e.stopPropagation();
41294         return false;
41295     },
41296     
41297     onTouchEnd: function(e, el)
41298     {
41299 //        e.preventDefault();
41300         
41301         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41302         
41303             this.leave(e,el);
41304             
41305             return;
41306         }
41307         
41308         if(!this.bgimage.length || !this.html.length){
41309             
41310             if(this.href.length){
41311                 window.location.href = this.href;
41312             }
41313             
41314             return;
41315         }
41316         
41317         if(!this.isFitContainer){
41318             return;
41319         }
41320         
41321         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41322         
41323         window.location.href = this.href;
41324     },
41325     
41326     //selection on single brick only
41327     selectBrick : function() {
41328         
41329         if (!this.parentId) {
41330             return;
41331         }
41332         
41333         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41334         var index = m.selectedBrick.indexOf(this.id);
41335         
41336         if ( index > -1) {
41337             m.selectedBrick.splice(index,1);
41338             this.el.removeClass(this.activeClass);
41339             return;
41340         }
41341         
41342         for(var i = 0; i < m.selectedBrick.length; i++) {
41343             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41344             b.el.removeClass(b.activeClass);
41345         }
41346         
41347         m.selectedBrick = [];
41348         
41349         m.selectedBrick.push(this.id);
41350         this.el.addClass(this.activeClass);
41351         return;
41352     },
41353     
41354     isSelected : function(){
41355         return this.el.hasClass(this.activeClass);
41356         
41357     }
41358 });
41359
41360 Roo.apply(Roo.bootstrap.MasonryBrick, {
41361     
41362     //groups: {},
41363     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41364      /**
41365     * register a Masonry Brick
41366     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41367     */
41368     
41369     register : function(brick)
41370     {
41371         //this.groups[brick.id] = brick;
41372         this.groups.add(brick.id, brick);
41373     },
41374     /**
41375     * fetch a  masonry brick based on the masonry brick ID
41376     * @param {string} the masonry brick to add
41377     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41378     */
41379     
41380     get: function(brick_id) 
41381     {
41382         // if (typeof(this.groups[brick_id]) == 'undefined') {
41383         //     return false;
41384         // }
41385         // return this.groups[brick_id] ;
41386         
41387         if(this.groups.key(brick_id)) {
41388             return this.groups.key(brick_id);
41389         }
41390         
41391         return false;
41392     }
41393     
41394     
41395     
41396 });
41397
41398  /*
41399  * - LGPL
41400  *
41401  * element
41402  * 
41403  */
41404
41405 /**
41406  * @class Roo.bootstrap.Brick
41407  * @extends Roo.bootstrap.Component
41408  * Bootstrap Brick class
41409  * 
41410  * @constructor
41411  * Create a new Brick
41412  * @param {Object} config The config object
41413  */
41414
41415 Roo.bootstrap.Brick = function(config){
41416     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41417     
41418     this.addEvents({
41419         // raw events
41420         /**
41421          * @event click
41422          * When a Brick is click
41423          * @param {Roo.bootstrap.Brick} this
41424          * @param {Roo.EventObject} e
41425          */
41426         "click" : true
41427     });
41428 };
41429
41430 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41431     
41432     /**
41433      * @cfg {String} title
41434      */   
41435     title : '',
41436     /**
41437      * @cfg {String} html
41438      */   
41439     html : '',
41440     /**
41441      * @cfg {String} bgimage
41442      */   
41443     bgimage : '',
41444     /**
41445      * @cfg {String} cls
41446      */   
41447     cls : '',
41448     /**
41449      * @cfg {String} href
41450      */   
41451     href : '',
41452     /**
41453      * @cfg {String} video
41454      */   
41455     video : '',
41456     /**
41457      * @cfg {Boolean} square
41458      */   
41459     square : true,
41460     
41461     getAutoCreate : function()
41462     {
41463         var cls = 'roo-brick';
41464         
41465         if(this.href.length){
41466             cls += ' roo-brick-link';
41467         }
41468         
41469         if(this.bgimage.length){
41470             cls += ' roo-brick-image';
41471         }
41472         
41473         if(!this.html.length && !this.bgimage.length){
41474             cls += ' roo-brick-center-title';
41475         }
41476         
41477         if(!this.html.length && this.bgimage.length){
41478             cls += ' roo-brick-bottom-title';
41479         }
41480         
41481         if(this.cls){
41482             cls += ' ' + this.cls;
41483         }
41484         
41485         var cfg = {
41486             tag: (this.href.length) ? 'a' : 'div',
41487             cls: cls,
41488             cn: [
41489                 {
41490                     tag: 'div',
41491                     cls: 'roo-brick-paragraph',
41492                     cn: []
41493                 }
41494             ]
41495         };
41496         
41497         if(this.href.length){
41498             cfg.href = this.href;
41499         }
41500         
41501         var cn = cfg.cn[0].cn;
41502         
41503         if(this.title.length){
41504             cn.push({
41505                 tag: 'h4',
41506                 cls: 'roo-brick-title',
41507                 html: this.title
41508             });
41509         }
41510         
41511         if(this.html.length){
41512             cn.push({
41513                 tag: 'p',
41514                 cls: 'roo-brick-text',
41515                 html: this.html
41516             });
41517         } else {
41518             cn.cls += ' hide';
41519         }
41520         
41521         if(this.bgimage.length){
41522             cfg.cn.push({
41523                 tag: 'img',
41524                 cls: 'roo-brick-image-view',
41525                 src: this.bgimage
41526             });
41527         }
41528         
41529         return cfg;
41530     },
41531     
41532     initEvents: function() 
41533     {
41534         if(this.title.length || this.html.length){
41535             this.el.on('mouseenter'  ,this.enter, this);
41536             this.el.on('mouseleave', this.leave, this);
41537         }
41538         
41539         Roo.EventManager.onWindowResize(this.resize, this); 
41540         
41541         if(this.bgimage.length){
41542             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41543             this.imageEl.on('load', this.onImageLoad, this);
41544             return;
41545         }
41546         
41547         this.resize();
41548     },
41549     
41550     onImageLoad : function()
41551     {
41552         this.resize();
41553     },
41554     
41555     resize : function()
41556     {
41557         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41558         
41559         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41560         
41561         if(this.bgimage.length){
41562             var image = this.el.select('.roo-brick-image-view', true).first();
41563             
41564             image.setWidth(paragraph.getWidth());
41565             
41566             if(this.square){
41567                 image.setHeight(paragraph.getWidth());
41568             }
41569             
41570             this.el.setHeight(image.getHeight());
41571             paragraph.setHeight(image.getHeight());
41572             
41573         }
41574         
41575     },
41576     
41577     enter: function(e, el)
41578     {
41579         e.preventDefault();
41580         
41581         if(this.bgimage.length){
41582             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41583             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41584         }
41585     },
41586     
41587     leave: function(e, el)
41588     {
41589         e.preventDefault();
41590         
41591         if(this.bgimage.length){
41592             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41593             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41594         }
41595     }
41596     
41597 });
41598
41599  
41600
41601  /*
41602  * - LGPL
41603  *
41604  * Number field 
41605  */
41606
41607 /**
41608  * @class Roo.bootstrap.form.NumberField
41609  * @extends Roo.bootstrap.form.Input
41610  * Bootstrap NumberField class
41611  * 
41612  * 
41613  * 
41614  * 
41615  * @constructor
41616  * Create a new NumberField
41617  * @param {Object} config The config object
41618  */
41619
41620 Roo.bootstrap.form.NumberField = function(config){
41621     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41622 };
41623
41624 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41625     
41626     /**
41627      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41628      */
41629     allowDecimals : true,
41630     /**
41631      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41632      */
41633     decimalSeparator : ".",
41634     /**
41635      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41636      */
41637     decimalPrecision : 2,
41638     /**
41639      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41640      */
41641     allowNegative : true,
41642     
41643     /**
41644      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41645      */
41646     allowZero: true,
41647     /**
41648      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41649      */
41650     minValue : Number.NEGATIVE_INFINITY,
41651     /**
41652      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41653      */
41654     maxValue : Number.MAX_VALUE,
41655     /**
41656      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41657      */
41658     minText : "The minimum value for this field is {0}",
41659     /**
41660      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41661      */
41662     maxText : "The maximum value for this field is {0}",
41663     /**
41664      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41665      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41666      */
41667     nanText : "{0} is not a valid number",
41668     /**
41669      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41670      */
41671     thousandsDelimiter : false,
41672     /**
41673      * @cfg {String} valueAlign alignment of value
41674      */
41675     valueAlign : "left",
41676
41677     getAutoCreate : function()
41678     {
41679         var hiddenInput = {
41680             tag: 'input',
41681             type: 'hidden',
41682             id: Roo.id(),
41683             cls: 'hidden-number-input'
41684         };
41685         
41686         if (this.name) {
41687             hiddenInput.name = this.name;
41688         }
41689         
41690         this.name = '';
41691         
41692         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41693         
41694         this.name = hiddenInput.name;
41695         
41696         if(cfg.cn.length > 0) {
41697             cfg.cn.push(hiddenInput);
41698         }
41699         
41700         return cfg;
41701     },
41702
41703     // private
41704     initEvents : function()
41705     {   
41706         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41707         
41708         var allowed = "0123456789";
41709         
41710         if(this.allowDecimals){
41711             allowed += this.decimalSeparator;
41712         }
41713         
41714         if(this.allowNegative){
41715             allowed += "-";
41716         }
41717         
41718         if(this.thousandsDelimiter) {
41719             allowed += ",";
41720         }
41721         
41722         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41723         
41724         var keyPress = function(e){
41725             
41726             var k = e.getKey();
41727             
41728             var c = e.getCharCode();
41729             
41730             if(
41731                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41732                     allowed.indexOf(String.fromCharCode(c)) === -1
41733             ){
41734                 e.stopEvent();
41735                 return;
41736             }
41737             
41738             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41739                 return;
41740             }
41741             
41742             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41743                 e.stopEvent();
41744             }
41745         };
41746         
41747         this.el.on("keypress", keyPress, this);
41748     },
41749     
41750     validateValue : function(value)
41751     {
41752         
41753         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41754             return false;
41755         }
41756         
41757         var num = this.parseValue(value);
41758         
41759         if(isNaN(num)){
41760             this.markInvalid(String.format(this.nanText, value));
41761             return false;
41762         }
41763         
41764         if(num < this.minValue){
41765             this.markInvalid(String.format(this.minText, this.minValue));
41766             return false;
41767         }
41768         
41769         if(num > this.maxValue){
41770             this.markInvalid(String.format(this.maxText, this.maxValue));
41771             return false;
41772         }
41773         
41774         return true;
41775     },
41776
41777     getValue : function()
41778     {
41779         var v = this.hiddenEl().getValue();
41780         
41781         return this.fixPrecision(this.parseValue(v));
41782     },
41783
41784     parseValue : function(value)
41785     {
41786         if(this.thousandsDelimiter) {
41787             value += "";
41788             r = new RegExp(",", "g");
41789             value = value.replace(r, "");
41790         }
41791         
41792         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41793         return isNaN(value) ? '' : value;
41794     },
41795
41796     fixPrecision : function(value)
41797     {
41798         if(this.thousandsDelimiter) {
41799             value += "";
41800             r = new RegExp(",", "g");
41801             value = value.replace(r, "");
41802         }
41803         
41804         var nan = isNaN(value);
41805         
41806         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41807             return nan ? '' : value;
41808         }
41809         return parseFloat(value).toFixed(this.decimalPrecision);
41810     },
41811
41812     setValue : function(v)
41813     {
41814         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41815         
41816         this.value = v;
41817         
41818         if(this.rendered){
41819             
41820             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41821             
41822             this.inputEl().dom.value = (v == '') ? '' :
41823                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41824             
41825             if(!this.allowZero && v === '0') {
41826                 this.hiddenEl().dom.value = '';
41827                 this.inputEl().dom.value = '';
41828             }
41829             
41830             this.validate();
41831         }
41832     },
41833
41834     decimalPrecisionFcn : function(v)
41835     {
41836         return Math.floor(v);
41837     },
41838
41839     beforeBlur : function()
41840     {
41841         var v = this.parseValue(this.getRawValue());
41842         
41843         if(v || v === 0 || v === ''){
41844             this.setValue(v);
41845         }
41846     },
41847     
41848     hiddenEl : function()
41849     {
41850         return this.el.select('input.hidden-number-input',true).first();
41851     }
41852     
41853 });
41854
41855  
41856
41857 /*
41858 * Licence: LGPL
41859 */
41860
41861 /**
41862  * @class Roo.bootstrap.DocumentSlider
41863  * @extends Roo.bootstrap.Component
41864  * Bootstrap DocumentSlider class
41865  * 
41866  * @constructor
41867  * Create a new DocumentViewer
41868  * @param {Object} config The config object
41869  */
41870
41871 Roo.bootstrap.DocumentSlider = function(config){
41872     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41873     
41874     this.files = [];
41875     
41876     this.addEvents({
41877         /**
41878          * @event initial
41879          * Fire after initEvent
41880          * @param {Roo.bootstrap.DocumentSlider} this
41881          */
41882         "initial" : true,
41883         /**
41884          * @event update
41885          * Fire after update
41886          * @param {Roo.bootstrap.DocumentSlider} this
41887          */
41888         "update" : true,
41889         /**
41890          * @event click
41891          * Fire after click
41892          * @param {Roo.bootstrap.DocumentSlider} this
41893          */
41894         "click" : true
41895     });
41896 };
41897
41898 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41899     
41900     files : false,
41901     
41902     indicator : 0,
41903     
41904     getAutoCreate : function()
41905     {
41906         var cfg = {
41907             tag : 'div',
41908             cls : 'roo-document-slider',
41909             cn : [
41910                 {
41911                     tag : 'div',
41912                     cls : 'roo-document-slider-header',
41913                     cn : [
41914                         {
41915                             tag : 'div',
41916                             cls : 'roo-document-slider-header-title'
41917                         }
41918                     ]
41919                 },
41920                 {
41921                     tag : 'div',
41922                     cls : 'roo-document-slider-body',
41923                     cn : [
41924                         {
41925                             tag : 'div',
41926                             cls : 'roo-document-slider-prev',
41927                             cn : [
41928                                 {
41929                                     tag : 'i',
41930                                     cls : 'fa fa-chevron-left'
41931                                 }
41932                             ]
41933                         },
41934                         {
41935                             tag : 'div',
41936                             cls : 'roo-document-slider-thumb',
41937                             cn : [
41938                                 {
41939                                     tag : 'img',
41940                                     cls : 'roo-document-slider-image'
41941                                 }
41942                             ]
41943                         },
41944                         {
41945                             tag : 'div',
41946                             cls : 'roo-document-slider-next',
41947                             cn : [
41948                                 {
41949                                     tag : 'i',
41950                                     cls : 'fa fa-chevron-right'
41951                                 }
41952                             ]
41953                         }
41954                     ]
41955                 }
41956             ]
41957         };
41958         
41959         return cfg;
41960     },
41961     
41962     initEvents : function()
41963     {
41964         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41965         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41966         
41967         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41968         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41969         
41970         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41971         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41972         
41973         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41974         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41975         
41976         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41977         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41978         
41979         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41980         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41981         
41982         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41983         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41984         
41985         this.thumbEl.on('click', this.onClick, this);
41986         
41987         this.prevIndicator.on('click', this.prev, this);
41988         
41989         this.nextIndicator.on('click', this.next, this);
41990         
41991     },
41992     
41993     initial : function()
41994     {
41995         if(this.files.length){
41996             this.indicator = 1;
41997             this.update()
41998         }
41999         
42000         this.fireEvent('initial', this);
42001     },
42002     
42003     update : function()
42004     {
42005         this.imageEl.attr('src', this.files[this.indicator - 1]);
42006         
42007         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42008         
42009         this.prevIndicator.show();
42010         
42011         if(this.indicator == 1){
42012             this.prevIndicator.hide();
42013         }
42014         
42015         this.nextIndicator.show();
42016         
42017         if(this.indicator == this.files.length){
42018             this.nextIndicator.hide();
42019         }
42020         
42021         this.thumbEl.scrollTo('top');
42022         
42023         this.fireEvent('update', this);
42024     },
42025     
42026     onClick : function(e)
42027     {
42028         e.preventDefault();
42029         
42030         this.fireEvent('click', this);
42031     },
42032     
42033     prev : function(e)
42034     {
42035         e.preventDefault();
42036         
42037         this.indicator = Math.max(1, this.indicator - 1);
42038         
42039         this.update();
42040     },
42041     
42042     next : function(e)
42043     {
42044         e.preventDefault();
42045         
42046         this.indicator = Math.min(this.files.length, this.indicator + 1);
42047         
42048         this.update();
42049     }
42050 });
42051 /*
42052  * - LGPL
42053  *
42054  * RadioSet
42055  *
42056  *
42057  */
42058
42059 /**
42060  * @class Roo.bootstrap.form.RadioSet
42061  * @extends Roo.bootstrap.form.Input
42062  * @children Roo.bootstrap.form.Radio
42063  * Bootstrap RadioSet class
42064  * @cfg {String} indicatorpos (left|right) default left
42065  * @cfg {Boolean} inline (true|false) inline the element (default true)
42066  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42067  * @constructor
42068  * Create a new RadioSet
42069  * @param {Object} config The config object
42070  */
42071
42072 Roo.bootstrap.form.RadioSet = function(config){
42073     
42074     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42075     
42076     this.radioes = [];
42077     
42078     Roo.bootstrap.form.RadioSet.register(this);
42079     
42080     this.addEvents({
42081         /**
42082         * @event check
42083         * Fires when the element is checked or unchecked.
42084         * @param {Roo.bootstrap.form.RadioSet} this This radio
42085         * @param {Roo.bootstrap.form.Radio} item The checked item
42086         */
42087        check : true,
42088        /**
42089         * @event click
42090         * Fires when the element is click.
42091         * @param {Roo.bootstrap.form.RadioSet} this This radio set
42092         * @param {Roo.bootstrap.form.Radio} item The checked item
42093         * @param {Roo.EventObject} e The event object
42094         */
42095        click : true
42096     });
42097     
42098 };
42099
42100 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
42101
42102     radioes : false,
42103     
42104     inline : true,
42105     
42106     weight : '',
42107     
42108     indicatorpos : 'left',
42109     
42110     getAutoCreate : function()
42111     {
42112         var label = {
42113             tag : 'label',
42114             cls : 'roo-radio-set-label',
42115             cn : [
42116                 {
42117                     tag : 'span',
42118                     html : this.fieldLabel
42119                 }
42120             ]
42121         };
42122         if (Roo.bootstrap.version == 3) {
42123             
42124             
42125             if(this.indicatorpos == 'left'){
42126                 label.cn.unshift({
42127                     tag : 'i',
42128                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42129                     tooltip : 'This field is required'
42130                 });
42131             } else {
42132                 label.cn.push({
42133                     tag : 'i',
42134                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42135                     tooltip : 'This field is required'
42136                 });
42137             }
42138         }
42139         var items = {
42140             tag : 'div',
42141             cls : 'roo-radio-set-items'
42142         };
42143         
42144         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42145         
42146         if (align === 'left' && this.fieldLabel.length) {
42147             
42148             items = {
42149                 cls : "roo-radio-set-right", 
42150                 cn: [
42151                     items
42152                 ]
42153             };
42154             
42155             if(this.labelWidth > 12){
42156                 label.style = "width: " + this.labelWidth + 'px';
42157             }
42158             
42159             if(this.labelWidth < 13 && this.labelmd == 0){
42160                 this.labelmd = this.labelWidth;
42161             }
42162             
42163             if(this.labellg > 0){
42164                 label.cls += ' col-lg-' + this.labellg;
42165                 items.cls += ' col-lg-' + (12 - this.labellg);
42166             }
42167             
42168             if(this.labelmd > 0){
42169                 label.cls += ' col-md-' + this.labelmd;
42170                 items.cls += ' col-md-' + (12 - this.labelmd);
42171             }
42172             
42173             if(this.labelsm > 0){
42174                 label.cls += ' col-sm-' + this.labelsm;
42175                 items.cls += ' col-sm-' + (12 - this.labelsm);
42176             }
42177             
42178             if(this.labelxs > 0){
42179                 label.cls += ' col-xs-' + this.labelxs;
42180                 items.cls += ' col-xs-' + (12 - this.labelxs);
42181             }
42182         }
42183         
42184         var cfg = {
42185             tag : 'div',
42186             cls : 'roo-radio-set',
42187             cn : [
42188                 {
42189                     tag : 'input',
42190                     cls : 'roo-radio-set-input',
42191                     type : 'hidden',
42192                     name : this.name,
42193                     value : this.value ? this.value :  ''
42194                 },
42195                 label,
42196                 items
42197             ]
42198         };
42199         
42200         if(this.weight.length){
42201             cfg.cls += ' roo-radio-' + this.weight;
42202         }
42203         
42204         if(this.inline) {
42205             cfg.cls += ' roo-radio-set-inline';
42206         }
42207         
42208         var settings=this;
42209         ['xs','sm','md','lg'].map(function(size){
42210             if (settings[size]) {
42211                 cfg.cls += ' col-' + size + '-' + settings[size];
42212             }
42213         });
42214         
42215         return cfg;
42216         
42217     },
42218
42219     initEvents : function()
42220     {
42221         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42222         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42223         
42224         if(!this.fieldLabel.length){
42225             this.labelEl.hide();
42226         }
42227         
42228         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42229         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42230         
42231         this.indicator = this.indicatorEl();
42232         
42233         if(this.indicator){
42234             this.indicator.addClass('invisible');
42235         }
42236         
42237         this.originalValue = this.getValue();
42238         
42239     },
42240     
42241     inputEl: function ()
42242     {
42243         return this.el.select('.roo-radio-set-input', true).first();
42244     },
42245     
42246     getChildContainer : function()
42247     {
42248         return this.itemsEl;
42249     },
42250     
42251     register : function(item)
42252     {
42253         this.radioes.push(item);
42254         
42255     },
42256     
42257     validate : function()
42258     {   
42259         if(this.getVisibilityEl().hasClass('hidden')){
42260             return true;
42261         }
42262         
42263         var valid = false;
42264         
42265         Roo.each(this.radioes, function(i){
42266             if(!i.checked){
42267                 return;
42268             }
42269             
42270             valid = true;
42271             return false;
42272         });
42273         
42274         if(this.allowBlank) {
42275             return true;
42276         }
42277         
42278         if(this.disabled || valid){
42279             this.markValid();
42280             return true;
42281         }
42282         
42283         this.markInvalid();
42284         return false;
42285         
42286     },
42287     
42288     markValid : function()
42289     {
42290         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42291             this.indicatorEl().removeClass('visible');
42292             this.indicatorEl().addClass('invisible');
42293         }
42294         
42295         
42296         if (Roo.bootstrap.version == 3) {
42297             this.el.removeClass([this.invalidClass, this.validClass]);
42298             this.el.addClass(this.validClass);
42299         } else {
42300             this.el.removeClass(['is-invalid','is-valid']);
42301             this.el.addClass(['is-valid']);
42302         }
42303         this.fireEvent('valid', this);
42304     },
42305     
42306     markInvalid : function(msg)
42307     {
42308         if(this.allowBlank || this.disabled){
42309             return;
42310         }
42311         
42312         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42313             this.indicatorEl().removeClass('invisible');
42314             this.indicatorEl().addClass('visible');
42315         }
42316         if (Roo.bootstrap.version == 3) {
42317             this.el.removeClass([this.invalidClass, this.validClass]);
42318             this.el.addClass(this.invalidClass);
42319         } else {
42320             this.el.removeClass(['is-invalid','is-valid']);
42321             this.el.addClass(['is-invalid']);
42322         }
42323         
42324         this.fireEvent('invalid', this, msg);
42325         
42326     },
42327     
42328     setValue : function(v, suppressEvent)
42329     {   
42330         if(this.value === v){
42331             return;
42332         }
42333         
42334         this.value = v;
42335         
42336         if(this.rendered){
42337             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42338         }
42339         
42340         Roo.each(this.radioes, function(i){
42341             i.checked = false;
42342             i.el.removeClass('checked');
42343         });
42344         
42345         Roo.each(this.radioes, function(i){
42346             
42347             if(i.value === v || i.value.toString() === v.toString()){
42348                 i.checked = true;
42349                 i.el.addClass('checked');
42350                 
42351                 if(suppressEvent !== true){
42352                     this.fireEvent('check', this, i);
42353                 }
42354                 
42355                 return false;
42356             }
42357             
42358         }, this);
42359         
42360         this.validate();
42361     },
42362     
42363     clearInvalid : function(){
42364         
42365         if(!this.el || this.preventMark){
42366             return;
42367         }
42368         
42369         this.el.removeClass([this.invalidClass]);
42370         
42371         this.fireEvent('valid', this);
42372     }
42373     
42374 });
42375
42376 Roo.apply(Roo.bootstrap.form.RadioSet, {
42377     
42378     groups: {},
42379     
42380     register : function(set)
42381     {
42382         this.groups[set.name] = set;
42383     },
42384     
42385     get: function(name) 
42386     {
42387         if (typeof(this.groups[name]) == 'undefined') {
42388             return false;
42389         }
42390         
42391         return this.groups[name] ;
42392     }
42393     
42394 });
42395 /*
42396  * Based on:
42397  * Ext JS Library 1.1.1
42398  * Copyright(c) 2006-2007, Ext JS, LLC.
42399  *
42400  * Originally Released Under LGPL - original licence link has changed is not relivant.
42401  *
42402  * Fork - LGPL
42403  * <script type="text/javascript">
42404  */
42405
42406
42407 /**
42408  * @class Roo.bootstrap.SplitBar
42409  * @extends Roo.util.Observable
42410  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42411  * <br><br>
42412  * Usage:
42413  * <pre><code>
42414 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42415                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42416 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42417 split.minSize = 100;
42418 split.maxSize = 600;
42419 split.animate = true;
42420 split.on('moved', splitterMoved);
42421 </code></pre>
42422  * @constructor
42423  * Create a new SplitBar
42424  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42425  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42426  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42427  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42428                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42429                         position of the SplitBar).
42430  */
42431 Roo.bootstrap.SplitBar = function(cfg){
42432     
42433     /** @private */
42434     
42435     //{
42436     //  dragElement : elm
42437     //  resizingElement: el,
42438         // optional..
42439     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42440     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42441         // existingProxy ???
42442     //}
42443     
42444     this.el = Roo.get(cfg.dragElement, true);
42445     this.el.dom.unselectable = "on";
42446     /** @private */
42447     this.resizingEl = Roo.get(cfg.resizingElement, true);
42448
42449     /**
42450      * @private
42451      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42452      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42453      * @type Number
42454      */
42455     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42456     
42457     /**
42458      * The minimum size of the resizing element. (Defaults to 0)
42459      * @type Number
42460      */
42461     this.minSize = 0;
42462     
42463     /**
42464      * The maximum size of the resizing element. (Defaults to 2000)
42465      * @type Number
42466      */
42467     this.maxSize = 2000;
42468     
42469     /**
42470      * Whether to animate the transition to the new size
42471      * @type Boolean
42472      */
42473     this.animate = false;
42474     
42475     /**
42476      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42477      * @type Boolean
42478      */
42479     this.useShim = false;
42480     
42481     /** @private */
42482     this.shim = null;
42483     
42484     if(!cfg.existingProxy){
42485         /** @private */
42486         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42487     }else{
42488         this.proxy = Roo.get(cfg.existingProxy).dom;
42489     }
42490     /** @private */
42491     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42492     
42493     /** @private */
42494     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42495     
42496     /** @private */
42497     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42498     
42499     /** @private */
42500     this.dragSpecs = {};
42501     
42502     /**
42503      * @private The adapter to use to positon and resize elements
42504      */
42505     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42506     this.adapter.init(this);
42507     
42508     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42509         /** @private */
42510         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42511         this.el.addClass("roo-splitbar-h");
42512     }else{
42513         /** @private */
42514         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42515         this.el.addClass("roo-splitbar-v");
42516     }
42517     
42518     this.addEvents({
42519         /**
42520          * @event resize
42521          * Fires when the splitter is moved (alias for {@link #event-moved})
42522          * @param {Roo.bootstrap.SplitBar} this
42523          * @param {Number} newSize the new width or height
42524          */
42525         "resize" : true,
42526         /**
42527          * @event moved
42528          * Fires when the splitter is moved
42529          * @param {Roo.bootstrap.SplitBar} this
42530          * @param {Number} newSize the new width or height
42531          */
42532         "moved" : true,
42533         /**
42534          * @event beforeresize
42535          * Fires before the splitter is dragged
42536          * @param {Roo.bootstrap.SplitBar} this
42537          */
42538         "beforeresize" : true,
42539
42540         "beforeapply" : true
42541     });
42542
42543     Roo.util.Observable.call(this);
42544 };
42545
42546 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42547     onStartProxyDrag : function(x, y){
42548         this.fireEvent("beforeresize", this);
42549         if(!this.overlay){
42550             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42551             o.unselectable();
42552             o.enableDisplayMode("block");
42553             // all splitbars share the same overlay
42554             Roo.bootstrap.SplitBar.prototype.overlay = o;
42555         }
42556         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42557         this.overlay.show();
42558         Roo.get(this.proxy).setDisplayed("block");
42559         var size = this.adapter.getElementSize(this);
42560         this.activeMinSize = this.getMinimumSize();;
42561         this.activeMaxSize = this.getMaximumSize();;
42562         var c1 = size - this.activeMinSize;
42563         var c2 = Math.max(this.activeMaxSize - size, 0);
42564         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42565             this.dd.resetConstraints();
42566             this.dd.setXConstraint(
42567                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42568                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42569             );
42570             this.dd.setYConstraint(0, 0);
42571         }else{
42572             this.dd.resetConstraints();
42573             this.dd.setXConstraint(0, 0);
42574             this.dd.setYConstraint(
42575                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42576                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42577             );
42578          }
42579         this.dragSpecs.startSize = size;
42580         this.dragSpecs.startPoint = [x, y];
42581         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42582     },
42583     
42584     /** 
42585      * @private Called after the drag operation by the DDProxy
42586      */
42587     onEndProxyDrag : function(e){
42588         Roo.get(this.proxy).setDisplayed(false);
42589         var endPoint = Roo.lib.Event.getXY(e);
42590         if(this.overlay){
42591             this.overlay.hide();
42592         }
42593         var newSize;
42594         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42595             newSize = this.dragSpecs.startSize + 
42596                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42597                     endPoint[0] - this.dragSpecs.startPoint[0] :
42598                     this.dragSpecs.startPoint[0] - endPoint[0]
42599                 );
42600         }else{
42601             newSize = this.dragSpecs.startSize + 
42602                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42603                     endPoint[1] - this.dragSpecs.startPoint[1] :
42604                     this.dragSpecs.startPoint[1] - endPoint[1]
42605                 );
42606         }
42607         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42608         if(newSize != this.dragSpecs.startSize){
42609             if(this.fireEvent('beforeapply', this, newSize) !== false){
42610                 this.adapter.setElementSize(this, newSize);
42611                 this.fireEvent("moved", this, newSize);
42612                 this.fireEvent("resize", this, newSize);
42613             }
42614         }
42615     },
42616     
42617     /**
42618      * Get the adapter this SplitBar uses
42619      * @return The adapter object
42620      */
42621     getAdapter : function(){
42622         return this.adapter;
42623     },
42624     
42625     /**
42626      * Set the adapter this SplitBar uses
42627      * @param {Object} adapter A SplitBar adapter object
42628      */
42629     setAdapter : function(adapter){
42630         this.adapter = adapter;
42631         this.adapter.init(this);
42632     },
42633     
42634     /**
42635      * Gets the minimum size for the resizing element
42636      * @return {Number} The minimum size
42637      */
42638     getMinimumSize : function(){
42639         return this.minSize;
42640     },
42641     
42642     /**
42643      * Sets the minimum size for the resizing element
42644      * @param {Number} minSize The minimum size
42645      */
42646     setMinimumSize : function(minSize){
42647         this.minSize = minSize;
42648     },
42649     
42650     /**
42651      * Gets the maximum size for the resizing element
42652      * @return {Number} The maximum size
42653      */
42654     getMaximumSize : function(){
42655         return this.maxSize;
42656     },
42657     
42658     /**
42659      * Sets the maximum size for the resizing element
42660      * @param {Number} maxSize The maximum size
42661      */
42662     setMaximumSize : function(maxSize){
42663         this.maxSize = maxSize;
42664     },
42665     
42666     /**
42667      * Sets the initialize size for the resizing element
42668      * @param {Number} size The initial size
42669      */
42670     setCurrentSize : function(size){
42671         var oldAnimate = this.animate;
42672         this.animate = false;
42673         this.adapter.setElementSize(this, size);
42674         this.animate = oldAnimate;
42675     },
42676     
42677     /**
42678      * Destroy this splitbar. 
42679      * @param {Boolean} removeEl True to remove the element
42680      */
42681     destroy : function(removeEl){
42682         if(this.shim){
42683             this.shim.remove();
42684         }
42685         this.dd.unreg();
42686         this.proxy.parentNode.removeChild(this.proxy);
42687         if(removeEl){
42688             this.el.remove();
42689         }
42690     }
42691 });
42692
42693 /**
42694  * @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.
42695  */
42696 Roo.bootstrap.SplitBar.createProxy = function(dir){
42697     var proxy = new Roo.Element(document.createElement("div"));
42698     proxy.unselectable();
42699     var cls = 'roo-splitbar-proxy';
42700     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42701     document.body.appendChild(proxy.dom);
42702     return proxy.dom;
42703 };
42704
42705 /** 
42706  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42707  * Default Adapter. It assumes the splitter and resizing element are not positioned
42708  * elements and only gets/sets the width of the element. Generally used for table based layouts.
42709  */
42710 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42711 };
42712
42713 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42714     // do nothing for now
42715     init : function(s){
42716     
42717     },
42718     /**
42719      * Called before drag operations to get the current size of the resizing element. 
42720      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42721      */
42722      getElementSize : function(s){
42723         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42724             return s.resizingEl.getWidth();
42725         }else{
42726             return s.resizingEl.getHeight();
42727         }
42728     },
42729     
42730     /**
42731      * Called after drag operations to set the size of the resizing element.
42732      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42733      * @param {Number} newSize The new size to set
42734      * @param {Function} onComplete A function to be invoked when resizing is complete
42735      */
42736     setElementSize : function(s, newSize, onComplete){
42737         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42738             if(!s.animate){
42739                 s.resizingEl.setWidth(newSize);
42740                 if(onComplete){
42741                     onComplete(s, newSize);
42742                 }
42743             }else{
42744                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42745             }
42746         }else{
42747             
42748             if(!s.animate){
42749                 s.resizingEl.setHeight(newSize);
42750                 if(onComplete){
42751                     onComplete(s, newSize);
42752                 }
42753             }else{
42754                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42755             }
42756         }
42757     }
42758 };
42759
42760 /** 
42761  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42762  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42763  * Adapter that  moves the splitter element to align with the resized sizing element. 
42764  * Used with an absolute positioned SplitBar.
42765  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42766  * document.body, make sure you assign an id to the body element.
42767  */
42768 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42769     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42770     this.container = Roo.get(container);
42771 };
42772
42773 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42774     init : function(s){
42775         this.basic.init(s);
42776     },
42777     
42778     getElementSize : function(s){
42779         return this.basic.getElementSize(s);
42780     },
42781     
42782     setElementSize : function(s, newSize, onComplete){
42783         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42784     },
42785     
42786     moveSplitter : function(s){
42787         var yes = Roo.bootstrap.SplitBar;
42788         switch(s.placement){
42789             case yes.LEFT:
42790                 s.el.setX(s.resizingEl.getRight());
42791                 break;
42792             case yes.RIGHT:
42793                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42794                 break;
42795             case yes.TOP:
42796                 s.el.setY(s.resizingEl.getBottom());
42797                 break;
42798             case yes.BOTTOM:
42799                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42800                 break;
42801         }
42802     }
42803 };
42804
42805 /**
42806  * Orientation constant - Create a vertical SplitBar
42807  * @static
42808  * @type Number
42809  */
42810 Roo.bootstrap.SplitBar.VERTICAL = 1;
42811
42812 /**
42813  * Orientation constant - Create a horizontal SplitBar
42814  * @static
42815  * @type Number
42816  */
42817 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42818
42819 /**
42820  * Placement constant - The resizing element is to the left of the splitter element
42821  * @static
42822  * @type Number
42823  */
42824 Roo.bootstrap.SplitBar.LEFT = 1;
42825
42826 /**
42827  * Placement constant - The resizing element is to the right of the splitter element
42828  * @static
42829  * @type Number
42830  */
42831 Roo.bootstrap.SplitBar.RIGHT = 2;
42832
42833 /**
42834  * Placement constant - The resizing element is positioned above the splitter element
42835  * @static
42836  * @type Number
42837  */
42838 Roo.bootstrap.SplitBar.TOP = 3;
42839
42840 /**
42841  * Placement constant - The resizing element is positioned under splitter element
42842  * @static
42843  * @type Number
42844  */
42845 Roo.bootstrap.SplitBar.BOTTOM = 4;
42846 /*
42847  * Based on:
42848  * Ext JS Library 1.1.1
42849  * Copyright(c) 2006-2007, Ext JS, LLC.
42850  *
42851  * Originally Released Under LGPL - original licence link has changed is not relivant.
42852  *
42853  * Fork - LGPL
42854  * <script type="text/javascript">
42855  */
42856
42857 /**
42858  * @class Roo.bootstrap.layout.Manager
42859  * @extends Roo.bootstrap.Component
42860  * @abstract
42861  * Base class for layout managers.
42862  */
42863 Roo.bootstrap.layout.Manager = function(config)
42864 {
42865     this.monitorWindowResize = true; // do this before we apply configuration.
42866     
42867     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42868
42869
42870
42871
42872
42873     /** false to disable window resize monitoring @type Boolean */
42874     
42875     this.regions = {};
42876     this.addEvents({
42877         /**
42878          * @event layout
42879          * Fires when a layout is performed.
42880          * @param {Roo.LayoutManager} this
42881          */
42882         "layout" : true,
42883         /**
42884          * @event regionresized
42885          * Fires when the user resizes a region.
42886          * @param {Roo.LayoutRegion} region The resized region
42887          * @param {Number} newSize The new size (width for east/west, height for north/south)
42888          */
42889         "regionresized" : true,
42890         /**
42891          * @event regioncollapsed
42892          * Fires when a region is collapsed.
42893          * @param {Roo.LayoutRegion} region The collapsed region
42894          */
42895         "regioncollapsed" : true,
42896         /**
42897          * @event regionexpanded
42898          * Fires when a region is expanded.
42899          * @param {Roo.LayoutRegion} region The expanded region
42900          */
42901         "regionexpanded" : true
42902     });
42903     this.updating = false;
42904
42905     if (config.el) {
42906         this.el = Roo.get(config.el);
42907         this.initEvents();
42908     }
42909
42910 };
42911
42912 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42913
42914
42915     regions : null,
42916
42917     monitorWindowResize : true,
42918
42919
42920     updating : false,
42921
42922
42923     onRender : function(ct, position)
42924     {
42925         if(!this.el){
42926             this.el = Roo.get(ct);
42927             this.initEvents();
42928         }
42929         //this.fireEvent('render',this);
42930     },
42931
42932
42933     initEvents: function()
42934     {
42935
42936
42937         // ie scrollbar fix
42938         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42939             document.body.scroll = "no";
42940         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42941             this.el.position('relative');
42942         }
42943         this.id = this.el.id;
42944         this.el.addClass("roo-layout-container");
42945         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42946         if(this.el.dom != document.body ) {
42947             this.el.on('resize', this.layout,this);
42948             this.el.on('show', this.layout,this);
42949         }
42950
42951     },
42952
42953     /**
42954      * Returns true if this layout is currently being updated
42955      * @return {Boolean}
42956      */
42957     isUpdating : function(){
42958         return this.updating;
42959     },
42960
42961     /**
42962      * Suspend the LayoutManager from doing auto-layouts while
42963      * making multiple add or remove calls
42964      */
42965     beginUpdate : function(){
42966         this.updating = true;
42967     },
42968
42969     /**
42970      * Restore auto-layouts and optionally disable the manager from performing a layout
42971      * @param {Boolean} noLayout true to disable a layout update
42972      */
42973     endUpdate : function(noLayout){
42974         this.updating = false;
42975         if(!noLayout){
42976             this.layout();
42977         }
42978     },
42979
42980     layout: function(){
42981         // abstract...
42982     },
42983
42984     onRegionResized : function(region, newSize){
42985         this.fireEvent("regionresized", region, newSize);
42986         this.layout();
42987     },
42988
42989     onRegionCollapsed : function(region){
42990         this.fireEvent("regioncollapsed", region);
42991     },
42992
42993     onRegionExpanded : function(region){
42994         this.fireEvent("regionexpanded", region);
42995     },
42996
42997     /**
42998      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42999      * performs box-model adjustments.
43000      * @return {Object} The size as an object {width: (the width), height: (the height)}
43001      */
43002     getViewSize : function()
43003     {
43004         var size;
43005         if(this.el.dom != document.body){
43006             size = this.el.getSize();
43007         }else{
43008             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43009         }
43010         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43011         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43012         return size;
43013     },
43014
43015     /**
43016      * Returns the Element this layout is bound to.
43017      * @return {Roo.Element}
43018      */
43019     getEl : function(){
43020         return this.el;
43021     },
43022
43023     /**
43024      * Returns the specified region.
43025      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43026      * @return {Roo.LayoutRegion}
43027      */
43028     getRegion : function(target){
43029         return this.regions[target.toLowerCase()];
43030     },
43031
43032     onWindowResize : function(){
43033         if(this.monitorWindowResize){
43034             this.layout();
43035         }
43036     }
43037 });
43038 /*
43039  * Based on:
43040  * Ext JS Library 1.1.1
43041  * Copyright(c) 2006-2007, Ext JS, LLC.
43042  *
43043  * Originally Released Under LGPL - original licence link has changed is not relivant.
43044  *
43045  * Fork - LGPL
43046  * <script type="text/javascript">
43047  */
43048 /**
43049  * @class Roo.bootstrap.layout.Border
43050  * @extends Roo.bootstrap.layout.Manager
43051  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43052  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43053  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43054  * please see: examples/bootstrap/nested.html<br><br>
43055  
43056 <b>The container the layout is rendered into can be either the body element or any other element.
43057 If it is not the body element, the container needs to either be an absolute positioned element,
43058 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43059 the container size if it is not the body element.</b>
43060
43061 * @constructor
43062 * Create a new Border
43063 * @param {Object} config Configuration options
43064  */
43065 Roo.bootstrap.layout.Border = function(config){
43066     config = config || {};
43067     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43068     
43069     
43070     
43071     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43072         if(config[region]){
43073             config[region].region = region;
43074             this.addRegion(config[region]);
43075         }
43076     },this);
43077     
43078 };
43079
43080 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
43081
43082 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43083     
43084         /**
43085          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43086          */
43087         /**
43088          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43089          */
43090         /**
43091          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43092          */
43093         /**
43094          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43095          */
43096         /**
43097          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43098          */
43099         
43100         
43101         
43102         
43103     parent : false, // this might point to a 'nest' or a ???
43104     
43105     /**
43106      * Creates and adds a new region if it doesn't already exist.
43107      * @param {String} target The target region key (north, south, east, west or center).
43108      * @param {Object} config The regions config object
43109      * @return {BorderLayoutRegion} The new region
43110      */
43111     addRegion : function(config)
43112     {
43113         if(!this.regions[config.region]){
43114             var r = this.factory(config);
43115             this.bindRegion(r);
43116         }
43117         return this.regions[config.region];
43118     },
43119
43120     // private (kinda)
43121     bindRegion : function(r){
43122         this.regions[r.config.region] = r;
43123         
43124         r.on("visibilitychange",    this.layout, this);
43125         r.on("paneladded",          this.layout, this);
43126         r.on("panelremoved",        this.layout, this);
43127         r.on("invalidated",         this.layout, this);
43128         r.on("resized",             this.onRegionResized, this);
43129         r.on("collapsed",           this.onRegionCollapsed, this);
43130         r.on("expanded",            this.onRegionExpanded, this);
43131     },
43132
43133     /**
43134      * Performs a layout update.
43135      */
43136     layout : function()
43137     {
43138         if(this.updating) {
43139             return;
43140         }
43141         
43142         // render all the rebions if they have not been done alreayd?
43143         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43144             if(this.regions[region] && !this.regions[region].bodyEl){
43145                 this.regions[region].onRender(this.el)
43146             }
43147         },this);
43148         
43149         var size = this.getViewSize();
43150         var w = size.width;
43151         var h = size.height;
43152         var centerW = w;
43153         var centerH = h;
43154         var centerY = 0;
43155         var centerX = 0;
43156         //var x = 0, y = 0;
43157
43158         var rs = this.regions;
43159         var north = rs["north"];
43160         var south = rs["south"]; 
43161         var west = rs["west"];
43162         var east = rs["east"];
43163         var center = rs["center"];
43164         //if(this.hideOnLayout){ // not supported anymore
43165             //c.el.setStyle("display", "none");
43166         //}
43167         if(north && north.isVisible()){
43168             var b = north.getBox();
43169             var m = north.getMargins();
43170             b.width = w - (m.left+m.right);
43171             b.x = m.left;
43172             b.y = m.top;
43173             centerY = b.height + b.y + m.bottom;
43174             centerH -= centerY;
43175             north.updateBox(this.safeBox(b));
43176         }
43177         if(south && south.isVisible()){
43178             var b = south.getBox();
43179             var m = south.getMargins();
43180             b.width = w - (m.left+m.right);
43181             b.x = m.left;
43182             var totalHeight = (b.height + m.top + m.bottom);
43183             b.y = h - totalHeight + m.top;
43184             centerH -= totalHeight;
43185             south.updateBox(this.safeBox(b));
43186         }
43187         if(west && west.isVisible()){
43188             var b = west.getBox();
43189             var m = west.getMargins();
43190             b.height = centerH - (m.top+m.bottom);
43191             b.x = m.left;
43192             b.y = centerY + m.top;
43193             var totalWidth = (b.width + m.left + m.right);
43194             centerX += totalWidth;
43195             centerW -= totalWidth;
43196             west.updateBox(this.safeBox(b));
43197         }
43198         if(east && east.isVisible()){
43199             var b = east.getBox();
43200             var m = east.getMargins();
43201             b.height = centerH - (m.top+m.bottom);
43202             var totalWidth = (b.width + m.left + m.right);
43203             b.x = w - totalWidth + m.left;
43204             b.y = centerY + m.top;
43205             centerW -= totalWidth;
43206             east.updateBox(this.safeBox(b));
43207         }
43208         if(center){
43209             var m = center.getMargins();
43210             var centerBox = {
43211                 x: centerX + m.left,
43212                 y: centerY + m.top,
43213                 width: centerW - (m.left+m.right),
43214                 height: centerH - (m.top+m.bottom)
43215             };
43216             //if(this.hideOnLayout){
43217                 //center.el.setStyle("display", "block");
43218             //}
43219             center.updateBox(this.safeBox(centerBox));
43220         }
43221         this.el.repaint();
43222         this.fireEvent("layout", this);
43223     },
43224
43225     // private
43226     safeBox : function(box){
43227         box.width = Math.max(0, box.width);
43228         box.height = Math.max(0, box.height);
43229         return box;
43230     },
43231
43232     /**
43233      * Adds a ContentPanel (or subclass) to this layout.
43234      * @param {String} target The target region key (north, south, east, west or center).
43235      * @param {Roo.ContentPanel} panel The panel to add
43236      * @return {Roo.ContentPanel} The added panel
43237      */
43238     add : function(target, panel){
43239          
43240         target = target.toLowerCase();
43241         return this.regions[target].add(panel);
43242     },
43243
43244     /**
43245      * Remove a ContentPanel (or subclass) to this layout.
43246      * @param {String} target The target region key (north, south, east, west or center).
43247      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43248      * @return {Roo.ContentPanel} The removed panel
43249      */
43250     remove : function(target, panel){
43251         target = target.toLowerCase();
43252         return this.regions[target].remove(panel);
43253     },
43254
43255     /**
43256      * Searches all regions for a panel with the specified id
43257      * @param {String} panelId
43258      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43259      */
43260     findPanel : function(panelId){
43261         var rs = this.regions;
43262         for(var target in rs){
43263             if(typeof rs[target] != "function"){
43264                 var p = rs[target].getPanel(panelId);
43265                 if(p){
43266                     return p;
43267                 }
43268             }
43269         }
43270         return null;
43271     },
43272
43273     /**
43274      * Searches all regions for a panel with the specified id and activates (shows) it.
43275      * @param {String/ContentPanel} panelId The panels id or the panel itself
43276      * @return {Roo.ContentPanel} The shown panel or null
43277      */
43278     showPanel : function(panelId) {
43279       var rs = this.regions;
43280       for(var target in rs){
43281          var r = rs[target];
43282          if(typeof r != "function"){
43283             if(r.hasPanel(panelId)){
43284                return r.showPanel(panelId);
43285             }
43286          }
43287       }
43288       return null;
43289    },
43290
43291    /**
43292      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43293      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43294      */
43295    /*
43296     restoreState : function(provider){
43297         if(!provider){
43298             provider = Roo.state.Manager;
43299         }
43300         var sm = new Roo.LayoutStateManager();
43301         sm.init(this, provider);
43302     },
43303 */
43304  
43305  
43306     /**
43307      * Adds a xtype elements to the layout.
43308      * <pre><code>
43309
43310 layout.addxtype({
43311        xtype : 'ContentPanel',
43312        region: 'west',
43313        items: [ .... ]
43314    }
43315 );
43316
43317 layout.addxtype({
43318         xtype : 'NestedLayoutPanel',
43319         region: 'west',
43320         layout: {
43321            center: { },
43322            west: { }   
43323         },
43324         items : [ ... list of content panels or nested layout panels.. ]
43325    }
43326 );
43327 </code></pre>
43328      * @param {Object} cfg Xtype definition of item to add.
43329      */
43330     addxtype : function(cfg)
43331     {
43332         // basically accepts a pannel...
43333         // can accept a layout region..!?!?
43334         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43335         
43336         
43337         // theory?  children can only be panels??
43338         
43339         //if (!cfg.xtype.match(/Panel$/)) {
43340         //    return false;
43341         //}
43342         var ret = false;
43343         
43344         if (typeof(cfg.region) == 'undefined') {
43345             Roo.log("Failed to add Panel, region was not set");
43346             Roo.log(cfg);
43347             return false;
43348         }
43349         var region = cfg.region;
43350         delete cfg.region;
43351         
43352           
43353         var xitems = [];
43354         if (cfg.items) {
43355             xitems = cfg.items;
43356             delete cfg.items;
43357         }
43358         var nb = false;
43359         
43360         if ( region == 'center') {
43361             Roo.log("Center: " + cfg.title);
43362         }
43363         
43364         
43365         switch(cfg.xtype) 
43366         {
43367             case 'Content':  // ContentPanel (el, cfg)
43368             case 'Scroll':  // ContentPanel (el, cfg)
43369             case 'View': 
43370                 cfg.autoCreate = cfg.autoCreate || true;
43371                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43372                 //} else {
43373                 //    var el = this.el.createChild();
43374                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43375                 //}
43376                 
43377                 this.add(region, ret);
43378                 break;
43379             
43380             /*
43381             case 'TreePanel': // our new panel!
43382                 cfg.el = this.el.createChild();
43383                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43384                 this.add(region, ret);
43385                 break;
43386             */
43387             
43388             case 'Nest': 
43389                 // create a new Layout (which is  a Border Layout...
43390                 
43391                 var clayout = cfg.layout;
43392                 clayout.el  = this.el.createChild();
43393                 clayout.items   = clayout.items  || [];
43394                 
43395                 delete cfg.layout;
43396                 
43397                 // replace this exitems with the clayout ones..
43398                 xitems = clayout.items;
43399                  
43400                 // force background off if it's in center...
43401                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43402                     cfg.background = false;
43403                 }
43404                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43405                 
43406                 
43407                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43408                 //console.log('adding nested layout panel '  + cfg.toSource());
43409                 this.add(region, ret);
43410                 nb = {}; /// find first...
43411                 break;
43412             
43413             case 'Grid':
43414                 
43415                 // needs grid and region
43416                 
43417                 //var el = this.getRegion(region).el.createChild();
43418                 /*
43419                  *var el = this.el.createChild();
43420                 // create the grid first...
43421                 cfg.grid.container = el;
43422                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43423                 */
43424                 
43425                 if (region == 'center' && this.active ) {
43426                     cfg.background = false;
43427                 }
43428                 
43429                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43430                 
43431                 this.add(region, ret);
43432                 /*
43433                 if (cfg.background) {
43434                     // render grid on panel activation (if panel background)
43435                     ret.on('activate', function(gp) {
43436                         if (!gp.grid.rendered) {
43437                     //        gp.grid.render(el);
43438                         }
43439                     });
43440                 } else {
43441                   //  cfg.grid.render(el);
43442                 }
43443                 */
43444                 break;
43445            
43446            
43447             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43448                 // it was the old xcomponent building that caused this before.
43449                 // espeically if border is the top element in the tree.
43450                 ret = this;
43451                 break; 
43452                 
43453                     
43454                 
43455                 
43456                 
43457             default:
43458                 /*
43459                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43460                     
43461                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43462                     this.add(region, ret);
43463                 } else {
43464                 */
43465                     Roo.log(cfg);
43466                     throw "Can not add '" + cfg.xtype + "' to Border";
43467                     return null;
43468              
43469                                 
43470              
43471         }
43472         this.beginUpdate();
43473         // add children..
43474         var region = '';
43475         var abn = {};
43476         Roo.each(xitems, function(i)  {
43477             region = nb && i.region ? i.region : false;
43478             
43479             var add = ret.addxtype(i);
43480            
43481             if (region) {
43482                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43483                 if (!i.background) {
43484                     abn[region] = nb[region] ;
43485                 }
43486             }
43487             
43488         });
43489         this.endUpdate();
43490
43491         // make the last non-background panel active..
43492         //if (nb) { Roo.log(abn); }
43493         if (nb) {
43494             
43495             for(var r in abn) {
43496                 region = this.getRegion(r);
43497                 if (region) {
43498                     // tried using nb[r], but it does not work..
43499                      
43500                     region.showPanel(abn[r]);
43501                    
43502                 }
43503             }
43504         }
43505         return ret;
43506         
43507     },
43508     
43509     
43510 // private
43511     factory : function(cfg)
43512     {
43513         
43514         var validRegions = Roo.bootstrap.layout.Border.regions;
43515
43516         var target = cfg.region;
43517         cfg.mgr = this;
43518         
43519         var r = Roo.bootstrap.layout;
43520         Roo.log(target);
43521         switch(target){
43522             case "north":
43523                 return new r.North(cfg);
43524             case "south":
43525                 return new r.South(cfg);
43526             case "east":
43527                 return new r.East(cfg);
43528             case "west":
43529                 return new r.West(cfg);
43530             case "center":
43531                 return new r.Center(cfg);
43532         }
43533         throw 'Layout region "'+target+'" not supported.';
43534     }
43535     
43536     
43537 });
43538  /*
43539  * Based on:
43540  * Ext JS Library 1.1.1
43541  * Copyright(c) 2006-2007, Ext JS, LLC.
43542  *
43543  * Originally Released Under LGPL - original licence link has changed is not relivant.
43544  *
43545  * Fork - LGPL
43546  * <script type="text/javascript">
43547  */
43548  
43549 /**
43550  * @class Roo.bootstrap.layout.Basic
43551  * @extends Roo.util.Observable
43552  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43553  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43554  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43555  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43556  * @cfg {string}   region  the region that it inhabits..
43557  * @cfg {bool}   skipConfig skip config?
43558  * 
43559
43560  */
43561 Roo.bootstrap.layout.Basic = function(config){
43562     
43563     this.mgr = config.mgr;
43564     
43565     this.position = config.region;
43566     
43567     var skipConfig = config.skipConfig;
43568     
43569     this.events = {
43570         /**
43571          * @scope Roo.BasicLayoutRegion
43572          */
43573         
43574         /**
43575          * @event beforeremove
43576          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43577          * @param {Roo.LayoutRegion} this
43578          * @param {Roo.ContentPanel} panel The panel
43579          * @param {Object} e The cancel event object
43580          */
43581         "beforeremove" : true,
43582         /**
43583          * @event invalidated
43584          * Fires when the layout for this region is changed.
43585          * @param {Roo.LayoutRegion} this
43586          */
43587         "invalidated" : true,
43588         /**
43589          * @event visibilitychange
43590          * Fires when this region is shown or hidden 
43591          * @param {Roo.LayoutRegion} this
43592          * @param {Boolean} visibility true or false
43593          */
43594         "visibilitychange" : true,
43595         /**
43596          * @event paneladded
43597          * Fires when a panel is added. 
43598          * @param {Roo.LayoutRegion} this
43599          * @param {Roo.ContentPanel} panel The panel
43600          */
43601         "paneladded" : true,
43602         /**
43603          * @event panelremoved
43604          * Fires when a panel is removed. 
43605          * @param {Roo.LayoutRegion} this
43606          * @param {Roo.ContentPanel} panel The panel
43607          */
43608         "panelremoved" : true,
43609         /**
43610          * @event beforecollapse
43611          * Fires when this region before collapse.
43612          * @param {Roo.LayoutRegion} this
43613          */
43614         "beforecollapse" : true,
43615         /**
43616          * @event collapsed
43617          * Fires when this region is collapsed.
43618          * @param {Roo.LayoutRegion} this
43619          */
43620         "collapsed" : true,
43621         /**
43622          * @event expanded
43623          * Fires when this region is expanded.
43624          * @param {Roo.LayoutRegion} this
43625          */
43626         "expanded" : true,
43627         /**
43628          * @event slideshow
43629          * Fires when this region is slid into view.
43630          * @param {Roo.LayoutRegion} this
43631          */
43632         "slideshow" : true,
43633         /**
43634          * @event slidehide
43635          * Fires when this region slides out of view. 
43636          * @param {Roo.LayoutRegion} this
43637          */
43638         "slidehide" : true,
43639         /**
43640          * @event panelactivated
43641          * Fires when a panel is activated. 
43642          * @param {Roo.LayoutRegion} this
43643          * @param {Roo.ContentPanel} panel The activated panel
43644          */
43645         "panelactivated" : true,
43646         /**
43647          * @event resized
43648          * Fires when the user resizes this region. 
43649          * @param {Roo.LayoutRegion} this
43650          * @param {Number} newSize The new size (width for east/west, height for north/south)
43651          */
43652         "resized" : true
43653     };
43654     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43655     this.panels = new Roo.util.MixedCollection();
43656     this.panels.getKey = this.getPanelId.createDelegate(this);
43657     this.box = null;
43658     this.activePanel = null;
43659     // ensure listeners are added...
43660     
43661     if (config.listeners || config.events) {
43662         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43663             listeners : config.listeners || {},
43664             events : config.events || {}
43665         });
43666     }
43667     
43668     if(skipConfig !== true){
43669         this.applyConfig(config);
43670     }
43671 };
43672
43673 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43674 {
43675     getPanelId : function(p){
43676         return p.getId();
43677     },
43678     
43679     applyConfig : function(config){
43680         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43681         this.config = config;
43682         
43683     },
43684     
43685     /**
43686      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43687      * the width, for horizontal (north, south) the height.
43688      * @param {Number} newSize The new width or height
43689      */
43690     resizeTo : function(newSize){
43691         var el = this.el ? this.el :
43692                  (this.activePanel ? this.activePanel.getEl() : null);
43693         if(el){
43694             switch(this.position){
43695                 case "east":
43696                 case "west":
43697                     el.setWidth(newSize);
43698                     this.fireEvent("resized", this, newSize);
43699                 break;
43700                 case "north":
43701                 case "south":
43702                     el.setHeight(newSize);
43703                     this.fireEvent("resized", this, newSize);
43704                 break;                
43705             }
43706         }
43707     },
43708     
43709     getBox : function(){
43710         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43711     },
43712     
43713     getMargins : function(){
43714         return this.margins;
43715     },
43716     
43717     updateBox : function(box){
43718         this.box = box;
43719         var el = this.activePanel.getEl();
43720         el.dom.style.left = box.x + "px";
43721         el.dom.style.top = box.y + "px";
43722         this.activePanel.setSize(box.width, box.height);
43723     },
43724     
43725     /**
43726      * Returns the container element for this region.
43727      * @return {Roo.Element}
43728      */
43729     getEl : function(){
43730         return this.activePanel;
43731     },
43732     
43733     /**
43734      * Returns true if this region is currently visible.
43735      * @return {Boolean}
43736      */
43737     isVisible : function(){
43738         return this.activePanel ? true : false;
43739     },
43740     
43741     setActivePanel : function(panel){
43742         panel = this.getPanel(panel);
43743         if(this.activePanel && this.activePanel != panel){
43744             this.activePanel.setActiveState(false);
43745             this.activePanel.getEl().setLeftTop(-10000,-10000);
43746         }
43747         this.activePanel = panel;
43748         panel.setActiveState(true);
43749         if(this.box){
43750             panel.setSize(this.box.width, this.box.height);
43751         }
43752         this.fireEvent("panelactivated", this, panel);
43753         this.fireEvent("invalidated");
43754     },
43755     
43756     /**
43757      * Show the specified panel.
43758      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43759      * @return {Roo.ContentPanel} The shown panel or null
43760      */
43761     showPanel : function(panel){
43762         panel = this.getPanel(panel);
43763         if(panel){
43764             this.setActivePanel(panel);
43765         }
43766         return panel;
43767     },
43768     
43769     /**
43770      * Get the active panel for this region.
43771      * @return {Roo.ContentPanel} The active panel or null
43772      */
43773     getActivePanel : function(){
43774         return this.activePanel;
43775     },
43776     
43777     /**
43778      * Add the passed ContentPanel(s)
43779      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43780      * @return {Roo.ContentPanel} The panel added (if only one was added)
43781      */
43782     add : function(panel){
43783         if(arguments.length > 1){
43784             for(var i = 0, len = arguments.length; i < len; i++) {
43785                 this.add(arguments[i]);
43786             }
43787             return null;
43788         }
43789         if(this.hasPanel(panel)){
43790             this.showPanel(panel);
43791             return panel;
43792         }
43793         var el = panel.getEl();
43794         if(el.dom.parentNode != this.mgr.el.dom){
43795             this.mgr.el.dom.appendChild(el.dom);
43796         }
43797         if(panel.setRegion){
43798             panel.setRegion(this);
43799         }
43800         this.panels.add(panel);
43801         el.setStyle("position", "absolute");
43802         if(!panel.background){
43803             this.setActivePanel(panel);
43804             if(this.config.initialSize && this.panels.getCount()==1){
43805                 this.resizeTo(this.config.initialSize);
43806             }
43807         }
43808         this.fireEvent("paneladded", this, panel);
43809         return panel;
43810     },
43811     
43812     /**
43813      * Returns true if the panel is in this region.
43814      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43815      * @return {Boolean}
43816      */
43817     hasPanel : function(panel){
43818         if(typeof panel == "object"){ // must be panel obj
43819             panel = panel.getId();
43820         }
43821         return this.getPanel(panel) ? true : false;
43822     },
43823     
43824     /**
43825      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43826      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43827      * @param {Boolean} preservePanel Overrides the config preservePanel option
43828      * @return {Roo.ContentPanel} The panel that was removed
43829      */
43830     remove : function(panel, preservePanel){
43831         panel = this.getPanel(panel);
43832         if(!panel){
43833             return null;
43834         }
43835         var e = {};
43836         this.fireEvent("beforeremove", this, panel, e);
43837         if(e.cancel === true){
43838             return null;
43839         }
43840         var panelId = panel.getId();
43841         this.panels.removeKey(panelId);
43842         return panel;
43843     },
43844     
43845     /**
43846      * Returns the panel specified or null if it's not in this region.
43847      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43848      * @return {Roo.ContentPanel}
43849      */
43850     getPanel : function(id){
43851         if(typeof id == "object"){ // must be panel obj
43852             return id;
43853         }
43854         return this.panels.get(id);
43855     },
43856     
43857     /**
43858      * Returns this regions position (north/south/east/west/center).
43859      * @return {String} 
43860      */
43861     getPosition: function(){
43862         return this.position;    
43863     }
43864 });/*
43865  * Based on:
43866  * Ext JS Library 1.1.1
43867  * Copyright(c) 2006-2007, Ext JS, LLC.
43868  *
43869  * Originally Released Under LGPL - original licence link has changed is not relivant.
43870  *
43871  * Fork - LGPL
43872  * <script type="text/javascript">
43873  */
43874  
43875 /**
43876  * @class Roo.bootstrap.layout.Region
43877  * @extends Roo.bootstrap.layout.Basic
43878  * This class represents a region in a layout manager.
43879  
43880  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43881  * @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})
43882  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43883  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43884  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43885  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43886  * @cfg {String}    title           The title for the region (overrides panel titles)
43887  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43888  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43889  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43890  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43891  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43892  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43893  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43894  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43895  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43896  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43897
43898  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43899  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43900  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43901  * @cfg {Number}    width           For East/West panels
43902  * @cfg {Number}    height          For North/South panels
43903  * @cfg {Boolean}   split           To show the splitter
43904  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43905  * 
43906  * @cfg {string}   cls             Extra CSS classes to add to region
43907  * 
43908  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43909  * @cfg {string}   region  the region that it inhabits..
43910  *
43911
43912  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43913  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43914
43915  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43916  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43917  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43918  */
43919 Roo.bootstrap.layout.Region = function(config)
43920 {
43921     this.applyConfig(config);
43922
43923     var mgr = config.mgr;
43924     var pos = config.region;
43925     config.skipConfig = true;
43926     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43927     
43928     if (mgr.el) {
43929         this.onRender(mgr.el);   
43930     }
43931      
43932     this.visible = true;
43933     this.collapsed = false;
43934     this.unrendered_panels = [];
43935 };
43936
43937 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43938
43939     position: '', // set by wrapper (eg. north/south etc..)
43940     unrendered_panels : null,  // unrendered panels.
43941     
43942     tabPosition : false,
43943     
43944     mgr: false, // points to 'Border'
43945     
43946     
43947     createBody : function(){
43948         /** This region's body element 
43949         * @type Roo.Element */
43950         this.bodyEl = this.el.createChild({
43951                 tag: "div",
43952                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43953         });
43954     },
43955
43956     onRender: function(ctr, pos)
43957     {
43958         var dh = Roo.DomHelper;
43959         /** This region's container element 
43960         * @type Roo.Element */
43961         this.el = dh.append(ctr.dom, {
43962                 tag: "div",
43963                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43964             }, true);
43965         /** This region's title element 
43966         * @type Roo.Element */
43967     
43968         this.titleEl = dh.append(this.el.dom,  {
43969                 tag: "div",
43970                 unselectable: "on",
43971                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43972                 children:[
43973                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43974                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43975                 ]
43976             }, true);
43977         
43978         this.titleEl.enableDisplayMode();
43979         /** This region's title text element 
43980         * @type HTMLElement */
43981         this.titleTextEl = this.titleEl.dom.firstChild;
43982         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43983         /*
43984         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43985         this.closeBtn.enableDisplayMode();
43986         this.closeBtn.on("click", this.closeClicked, this);
43987         this.closeBtn.hide();
43988     */
43989         this.createBody(this.config);
43990         if(this.config.hideWhenEmpty){
43991             this.hide();
43992             this.on("paneladded", this.validateVisibility, this);
43993             this.on("panelremoved", this.validateVisibility, this);
43994         }
43995         if(this.autoScroll){
43996             this.bodyEl.setStyle("overflow", "auto");
43997         }else{
43998             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43999         }
44000         //if(c.titlebar !== false){
44001             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44002                 this.titleEl.hide();
44003             }else{
44004                 this.titleEl.show();
44005                 if(this.config.title){
44006                     this.titleTextEl.innerHTML = this.config.title;
44007                 }
44008             }
44009         //}
44010         if(this.config.collapsed){
44011             this.collapse(true);
44012         }
44013         if(this.config.hidden){
44014             this.hide();
44015         }
44016         
44017         if (this.unrendered_panels && this.unrendered_panels.length) {
44018             for (var i =0;i< this.unrendered_panels.length; i++) {
44019                 this.add(this.unrendered_panels[i]);
44020             }
44021             this.unrendered_panels = null;
44022             
44023         }
44024         
44025     },
44026     
44027     applyConfig : function(c)
44028     {
44029         /*
44030          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44031             var dh = Roo.DomHelper;
44032             if(c.titlebar !== false){
44033                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44034                 this.collapseBtn.on("click", this.collapse, this);
44035                 this.collapseBtn.enableDisplayMode();
44036                 /*
44037                 if(c.showPin === true || this.showPin){
44038                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44039                     this.stickBtn.enableDisplayMode();
44040                     this.stickBtn.on("click", this.expand, this);
44041                     this.stickBtn.hide();
44042                 }
44043                 
44044             }
44045             */
44046             /** This region's collapsed element
44047             * @type Roo.Element */
44048             /*
44049              *
44050             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44051                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44052             ]}, true);
44053             
44054             if(c.floatable !== false){
44055                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44056                this.collapsedEl.on("click", this.collapseClick, this);
44057             }
44058
44059             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44060                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44061                    id: "message", unselectable: "on", style:{"float":"left"}});
44062                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44063              }
44064             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44065             this.expandBtn.on("click", this.expand, this);
44066             
44067         }
44068         
44069         if(this.collapseBtn){
44070             this.collapseBtn.setVisible(c.collapsible == true);
44071         }
44072         
44073         this.cmargins = c.cmargins || this.cmargins ||
44074                          (this.position == "west" || this.position == "east" ?
44075                              {top: 0, left: 2, right:2, bottom: 0} :
44076                              {top: 2, left: 0, right:0, bottom: 2});
44077         */
44078         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44079         
44080         
44081         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44082         
44083         this.autoScroll = c.autoScroll || false;
44084         
44085         
44086        
44087         
44088         this.duration = c.duration || .30;
44089         this.slideDuration = c.slideDuration || .45;
44090         this.config = c;
44091        
44092     },
44093     /**
44094      * Returns true if this region is currently visible.
44095      * @return {Boolean}
44096      */
44097     isVisible : function(){
44098         return this.visible;
44099     },
44100
44101     /**
44102      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44103      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44104      */
44105     //setCollapsedTitle : function(title){
44106     //    title = title || "&#160;";
44107      //   if(this.collapsedTitleTextEl){
44108       //      this.collapsedTitleTextEl.innerHTML = title;
44109        // }
44110     //},
44111
44112     getBox : function(){
44113         var b;
44114       //  if(!this.collapsed){
44115             b = this.el.getBox(false, true);
44116        // }else{
44117           //  b = this.collapsedEl.getBox(false, true);
44118         //}
44119         return b;
44120     },
44121
44122     getMargins : function(){
44123         return this.margins;
44124         //return this.collapsed ? this.cmargins : this.margins;
44125     },
44126 /*
44127     highlight : function(){
44128         this.el.addClass("x-layout-panel-dragover");
44129     },
44130
44131     unhighlight : function(){
44132         this.el.removeClass("x-layout-panel-dragover");
44133     },
44134 */
44135     updateBox : function(box)
44136     {
44137         if (!this.bodyEl) {
44138             return; // not rendered yet..
44139         }
44140         
44141         this.box = box;
44142         if(!this.collapsed){
44143             this.el.dom.style.left = box.x + "px";
44144             this.el.dom.style.top = box.y + "px";
44145             this.updateBody(box.width, box.height);
44146         }else{
44147             this.collapsedEl.dom.style.left = box.x + "px";
44148             this.collapsedEl.dom.style.top = box.y + "px";
44149             this.collapsedEl.setSize(box.width, box.height);
44150         }
44151         if(this.tabs){
44152             this.tabs.autoSizeTabs();
44153         }
44154     },
44155
44156     updateBody : function(w, h)
44157     {
44158         if(w !== null){
44159             this.el.setWidth(w);
44160             w -= this.el.getBorderWidth("rl");
44161             if(this.config.adjustments){
44162                 w += this.config.adjustments[0];
44163             }
44164         }
44165         if(h !== null && h > 0){
44166             this.el.setHeight(h);
44167             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44168             h -= this.el.getBorderWidth("tb");
44169             if(this.config.adjustments){
44170                 h += this.config.adjustments[1];
44171             }
44172             this.bodyEl.setHeight(h);
44173             if(this.tabs){
44174                 h = this.tabs.syncHeight(h);
44175             }
44176         }
44177         if(this.panelSize){
44178             w = w !== null ? w : this.panelSize.width;
44179             h = h !== null ? h : this.panelSize.height;
44180         }
44181         if(this.activePanel){
44182             var el = this.activePanel.getEl();
44183             w = w !== null ? w : el.getWidth();
44184             h = h !== null ? h : el.getHeight();
44185             this.panelSize = {width: w, height: h};
44186             this.activePanel.setSize(w, h);
44187         }
44188         if(Roo.isIE && this.tabs){
44189             this.tabs.el.repaint();
44190         }
44191     },
44192
44193     /**
44194      * Returns the container element for this region.
44195      * @return {Roo.Element}
44196      */
44197     getEl : function(){
44198         return this.el;
44199     },
44200
44201     /**
44202      * Hides this region.
44203      */
44204     hide : function(){
44205         //if(!this.collapsed){
44206             this.el.dom.style.left = "-2000px";
44207             this.el.hide();
44208         //}else{
44209          //   this.collapsedEl.dom.style.left = "-2000px";
44210          //   this.collapsedEl.hide();
44211        // }
44212         this.visible = false;
44213         this.fireEvent("visibilitychange", this, false);
44214     },
44215
44216     /**
44217      * Shows this region if it was previously hidden.
44218      */
44219     show : function(){
44220         //if(!this.collapsed){
44221             this.el.show();
44222         //}else{
44223         //    this.collapsedEl.show();
44224        // }
44225         this.visible = true;
44226         this.fireEvent("visibilitychange", this, true);
44227     },
44228 /*
44229     closeClicked : function(){
44230         if(this.activePanel){
44231             this.remove(this.activePanel);
44232         }
44233     },
44234
44235     collapseClick : function(e){
44236         if(this.isSlid){
44237            e.stopPropagation();
44238            this.slideIn();
44239         }else{
44240            e.stopPropagation();
44241            this.slideOut();
44242         }
44243     },
44244 */
44245     /**
44246      * Collapses this region.
44247      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44248      */
44249     /*
44250     collapse : function(skipAnim, skipCheck = false){
44251         if(this.collapsed) {
44252             return;
44253         }
44254         
44255         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44256             
44257             this.collapsed = true;
44258             if(this.split){
44259                 this.split.el.hide();
44260             }
44261             if(this.config.animate && skipAnim !== true){
44262                 this.fireEvent("invalidated", this);
44263                 this.animateCollapse();
44264             }else{
44265                 this.el.setLocation(-20000,-20000);
44266                 this.el.hide();
44267                 this.collapsedEl.show();
44268                 this.fireEvent("collapsed", this);
44269                 this.fireEvent("invalidated", this);
44270             }
44271         }
44272         
44273     },
44274 */
44275     animateCollapse : function(){
44276         // overridden
44277     },
44278
44279     /**
44280      * Expands this region if it was previously collapsed.
44281      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44282      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44283      */
44284     /*
44285     expand : function(e, skipAnim){
44286         if(e) {
44287             e.stopPropagation();
44288         }
44289         if(!this.collapsed || this.el.hasActiveFx()) {
44290             return;
44291         }
44292         if(this.isSlid){
44293             this.afterSlideIn();
44294             skipAnim = true;
44295         }
44296         this.collapsed = false;
44297         if(this.config.animate && skipAnim !== true){
44298             this.animateExpand();
44299         }else{
44300             this.el.show();
44301             if(this.split){
44302                 this.split.el.show();
44303             }
44304             this.collapsedEl.setLocation(-2000,-2000);
44305             this.collapsedEl.hide();
44306             this.fireEvent("invalidated", this);
44307             this.fireEvent("expanded", this);
44308         }
44309     },
44310 */
44311     animateExpand : function(){
44312         // overridden
44313     },
44314
44315     initTabs : function()
44316     {
44317         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44318         
44319         var ts = new Roo.bootstrap.panel.Tabs({
44320             el: this.bodyEl.dom,
44321             region : this,
44322             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
44323             disableTooltips: this.config.disableTabTips,
44324             toolbar : this.config.toolbar
44325         });
44326         
44327         if(this.config.hideTabs){
44328             ts.stripWrap.setDisplayed(false);
44329         }
44330         this.tabs = ts;
44331         ts.resizeTabs = this.config.resizeTabs === true;
44332         ts.minTabWidth = this.config.minTabWidth || 40;
44333         ts.maxTabWidth = this.config.maxTabWidth || 250;
44334         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44335         ts.monitorResize = false;
44336         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44337         ts.bodyEl.addClass('roo-layout-tabs-body');
44338         this.panels.each(this.initPanelAsTab, this);
44339     },
44340
44341     initPanelAsTab : function(panel){
44342         var ti = this.tabs.addTab(
44343             panel.getEl().id,
44344             panel.getTitle(),
44345             null,
44346             this.config.closeOnTab && panel.isClosable(),
44347             panel.tpl
44348         );
44349         if(panel.tabTip !== undefined){
44350             ti.setTooltip(panel.tabTip);
44351         }
44352         ti.on("activate", function(){
44353               this.setActivePanel(panel);
44354         }, this);
44355         
44356         if(this.config.closeOnTab){
44357             ti.on("beforeclose", function(t, e){
44358                 e.cancel = true;
44359                 this.remove(panel);
44360             }, this);
44361         }
44362         
44363         panel.tabItem = ti;
44364         
44365         return ti;
44366     },
44367
44368     updatePanelTitle : function(panel, title)
44369     {
44370         if(this.activePanel == panel){
44371             this.updateTitle(title);
44372         }
44373         if(this.tabs){
44374             var ti = this.tabs.getTab(panel.getEl().id);
44375             ti.setText(title);
44376             if(panel.tabTip !== undefined){
44377                 ti.setTooltip(panel.tabTip);
44378             }
44379         }
44380     },
44381
44382     updateTitle : function(title){
44383         if(this.titleTextEl && !this.config.title){
44384             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44385         }
44386     },
44387
44388     setActivePanel : function(panel)
44389     {
44390         panel = this.getPanel(panel);
44391         if(this.activePanel && this.activePanel != panel){
44392             if(this.activePanel.setActiveState(false) === false){
44393                 return;
44394             }
44395         }
44396         this.activePanel = panel;
44397         panel.setActiveState(true);
44398         if(this.panelSize){
44399             panel.setSize(this.panelSize.width, this.panelSize.height);
44400         }
44401         if(this.closeBtn){
44402             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44403         }
44404         this.updateTitle(panel.getTitle());
44405         if(this.tabs){
44406             this.fireEvent("invalidated", this);
44407         }
44408         this.fireEvent("panelactivated", this, panel);
44409     },
44410
44411     /**
44412      * Shows the specified panel.
44413      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44414      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44415      */
44416     showPanel : function(panel)
44417     {
44418         panel = this.getPanel(panel);
44419         if(panel){
44420             if(this.tabs){
44421                 var tab = this.tabs.getTab(panel.getEl().id);
44422                 if(tab.isHidden()){
44423                     this.tabs.unhideTab(tab.id);
44424                 }
44425                 tab.activate();
44426             }else{
44427                 this.setActivePanel(panel);
44428             }
44429         }
44430         return panel;
44431     },
44432
44433     /**
44434      * Get the active panel for this region.
44435      * @return {Roo.ContentPanel} The active panel or null
44436      */
44437     getActivePanel : function(){
44438         return this.activePanel;
44439     },
44440
44441     validateVisibility : function(){
44442         if(this.panels.getCount() < 1){
44443             this.updateTitle("&#160;");
44444             this.closeBtn.hide();
44445             this.hide();
44446         }else{
44447             if(!this.isVisible()){
44448                 this.show();
44449             }
44450         }
44451     },
44452
44453     /**
44454      * Adds the passed ContentPanel(s) to this region.
44455      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44456      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44457      */
44458     add : function(panel)
44459     {
44460         if(arguments.length > 1){
44461             for(var i = 0, len = arguments.length; i < len; i++) {
44462                 this.add(arguments[i]);
44463             }
44464             return null;
44465         }
44466         
44467         // if we have not been rendered yet, then we can not really do much of this..
44468         if (!this.bodyEl) {
44469             this.unrendered_panels.push(panel);
44470             return panel;
44471         }
44472         
44473         
44474         
44475         
44476         if(this.hasPanel(panel)){
44477             this.showPanel(panel);
44478             return panel;
44479         }
44480         panel.setRegion(this);
44481         this.panels.add(panel);
44482        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44483             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44484             // and hide them... ???
44485             this.bodyEl.dom.appendChild(panel.getEl().dom);
44486             if(panel.background !== true){
44487                 this.setActivePanel(panel);
44488             }
44489             this.fireEvent("paneladded", this, panel);
44490             return panel;
44491         }
44492         */
44493         if(!this.tabs){
44494             this.initTabs();
44495         }else{
44496             this.initPanelAsTab(panel);
44497         }
44498         
44499         
44500         if(panel.background !== true){
44501             this.tabs.activate(panel.getEl().id);
44502         }
44503         this.fireEvent("paneladded", this, panel);
44504         return panel;
44505     },
44506
44507     /**
44508      * Hides the tab for the specified panel.
44509      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44510      */
44511     hidePanel : function(panel){
44512         if(this.tabs && (panel = this.getPanel(panel))){
44513             this.tabs.hideTab(panel.getEl().id);
44514         }
44515     },
44516
44517     /**
44518      * Unhides the tab for a previously hidden panel.
44519      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44520      */
44521     unhidePanel : function(panel){
44522         if(this.tabs && (panel = this.getPanel(panel))){
44523             this.tabs.unhideTab(panel.getEl().id);
44524         }
44525     },
44526
44527     clearPanels : function(){
44528         while(this.panels.getCount() > 0){
44529              this.remove(this.panels.first());
44530         }
44531     },
44532
44533     /**
44534      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44535      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44536      * @param {Boolean} preservePanel Overrides the config preservePanel option
44537      * @return {Roo.ContentPanel} The panel that was removed
44538      */
44539     remove : function(panel, preservePanel)
44540     {
44541         panel = this.getPanel(panel);
44542         if(!panel){
44543             return null;
44544         }
44545         var e = {};
44546         this.fireEvent("beforeremove", this, panel, e);
44547         if(e.cancel === true){
44548             return null;
44549         }
44550         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44551         var panelId = panel.getId();
44552         this.panels.removeKey(panelId);
44553         if(preservePanel){
44554             document.body.appendChild(panel.getEl().dom);
44555         }
44556         if(this.tabs){
44557             this.tabs.removeTab(panel.getEl().id);
44558         }else if (!preservePanel){
44559             this.bodyEl.dom.removeChild(panel.getEl().dom);
44560         }
44561         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44562             var p = this.panels.first();
44563             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44564             tempEl.appendChild(p.getEl().dom);
44565             this.bodyEl.update("");
44566             this.bodyEl.dom.appendChild(p.getEl().dom);
44567             tempEl = null;
44568             this.updateTitle(p.getTitle());
44569             this.tabs = null;
44570             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44571             this.setActivePanel(p);
44572         }
44573         panel.setRegion(null);
44574         if(this.activePanel == panel){
44575             this.activePanel = null;
44576         }
44577         if(this.config.autoDestroy !== false && preservePanel !== true){
44578             try{panel.destroy();}catch(e){}
44579         }
44580         this.fireEvent("panelremoved", this, panel);
44581         return panel;
44582     },
44583
44584     /**
44585      * Returns the TabPanel component used by this region
44586      * @return {Roo.TabPanel}
44587      */
44588     getTabs : function(){
44589         return this.tabs;
44590     },
44591
44592     createTool : function(parentEl, className){
44593         var btn = Roo.DomHelper.append(parentEl, {
44594             tag: "div",
44595             cls: "x-layout-tools-button",
44596             children: [ {
44597                 tag: "div",
44598                 cls: "roo-layout-tools-button-inner " + className,
44599                 html: "&#160;"
44600             }]
44601         }, true);
44602         btn.addClassOnOver("roo-layout-tools-button-over");
44603         return btn;
44604     }
44605 });/*
44606  * Based on:
44607  * Ext JS Library 1.1.1
44608  * Copyright(c) 2006-2007, Ext JS, LLC.
44609  *
44610  * Originally Released Under LGPL - original licence link has changed is not relivant.
44611  *
44612  * Fork - LGPL
44613  * <script type="text/javascript">
44614  */
44615  
44616
44617
44618 /**
44619  * @class Roo.SplitLayoutRegion
44620  * @extends Roo.LayoutRegion
44621  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44622  */
44623 Roo.bootstrap.layout.Split = function(config){
44624     this.cursor = config.cursor;
44625     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44626 };
44627
44628 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44629 {
44630     splitTip : "Drag to resize.",
44631     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44632     useSplitTips : false,
44633
44634     applyConfig : function(config){
44635         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44636     },
44637     
44638     onRender : function(ctr,pos) {
44639         
44640         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44641         if(!this.config.split){
44642             return;
44643         }
44644         if(!this.split){
44645             
44646             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44647                             tag: "div",
44648                             id: this.el.id + "-split",
44649                             cls: "roo-layout-split roo-layout-split-"+this.position,
44650                             html: "&#160;"
44651             });
44652             /** The SplitBar for this region 
44653             * @type Roo.SplitBar */
44654             // does not exist yet...
44655             Roo.log([this.position, this.orientation]);
44656             
44657             this.split = new Roo.bootstrap.SplitBar({
44658                 dragElement : splitEl,
44659                 resizingElement: this.el,
44660                 orientation : this.orientation
44661             });
44662             
44663             this.split.on("moved", this.onSplitMove, this);
44664             this.split.useShim = this.config.useShim === true;
44665             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44666             if(this.useSplitTips){
44667                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44668             }
44669             //if(config.collapsible){
44670             //    this.split.el.on("dblclick", this.collapse,  this);
44671             //}
44672         }
44673         if(typeof this.config.minSize != "undefined"){
44674             this.split.minSize = this.config.minSize;
44675         }
44676         if(typeof this.config.maxSize != "undefined"){
44677             this.split.maxSize = this.config.maxSize;
44678         }
44679         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44680             this.hideSplitter();
44681         }
44682         
44683     },
44684
44685     getHMaxSize : function(){
44686          var cmax = this.config.maxSize || 10000;
44687          var center = this.mgr.getRegion("center");
44688          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44689     },
44690
44691     getVMaxSize : function(){
44692          var cmax = this.config.maxSize || 10000;
44693          var center = this.mgr.getRegion("center");
44694          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44695     },
44696
44697     onSplitMove : function(split, newSize){
44698         this.fireEvent("resized", this, newSize);
44699     },
44700     
44701     /** 
44702      * Returns the {@link Roo.SplitBar} for this region.
44703      * @return {Roo.SplitBar}
44704      */
44705     getSplitBar : function(){
44706         return this.split;
44707     },
44708     
44709     hide : function(){
44710         this.hideSplitter();
44711         Roo.bootstrap.layout.Split.superclass.hide.call(this);
44712     },
44713
44714     hideSplitter : function(){
44715         if(this.split){
44716             this.split.el.setLocation(-2000,-2000);
44717             this.split.el.hide();
44718         }
44719     },
44720
44721     show : function(){
44722         if(this.split){
44723             this.split.el.show();
44724         }
44725         Roo.bootstrap.layout.Split.superclass.show.call(this);
44726     },
44727     
44728     beforeSlide: function(){
44729         if(Roo.isGecko){// firefox overflow auto bug workaround
44730             this.bodyEl.clip();
44731             if(this.tabs) {
44732                 this.tabs.bodyEl.clip();
44733             }
44734             if(this.activePanel){
44735                 this.activePanel.getEl().clip();
44736                 
44737                 if(this.activePanel.beforeSlide){
44738                     this.activePanel.beforeSlide();
44739                 }
44740             }
44741         }
44742     },
44743     
44744     afterSlide : function(){
44745         if(Roo.isGecko){// firefox overflow auto bug workaround
44746             this.bodyEl.unclip();
44747             if(this.tabs) {
44748                 this.tabs.bodyEl.unclip();
44749             }
44750             if(this.activePanel){
44751                 this.activePanel.getEl().unclip();
44752                 if(this.activePanel.afterSlide){
44753                     this.activePanel.afterSlide();
44754                 }
44755             }
44756         }
44757     },
44758
44759     initAutoHide : function(){
44760         if(this.autoHide !== false){
44761             if(!this.autoHideHd){
44762                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44763                 this.autoHideHd = {
44764                     "mouseout": function(e){
44765                         if(!e.within(this.el, true)){
44766                             st.delay(500);
44767                         }
44768                     },
44769                     "mouseover" : function(e){
44770                         st.cancel();
44771                     },
44772                     scope : this
44773                 };
44774             }
44775             this.el.on(this.autoHideHd);
44776         }
44777     },
44778
44779     clearAutoHide : function(){
44780         if(this.autoHide !== false){
44781             this.el.un("mouseout", this.autoHideHd.mouseout);
44782             this.el.un("mouseover", this.autoHideHd.mouseover);
44783         }
44784     },
44785
44786     clearMonitor : function(){
44787         Roo.get(document).un("click", this.slideInIf, this);
44788     },
44789
44790     // these names are backwards but not changed for compat
44791     slideOut : function(){
44792         if(this.isSlid || this.el.hasActiveFx()){
44793             return;
44794         }
44795         this.isSlid = true;
44796         if(this.collapseBtn){
44797             this.collapseBtn.hide();
44798         }
44799         this.closeBtnState = this.closeBtn.getStyle('display');
44800         this.closeBtn.hide();
44801         if(this.stickBtn){
44802             this.stickBtn.show();
44803         }
44804         this.el.show();
44805         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44806         this.beforeSlide();
44807         this.el.setStyle("z-index", 10001);
44808         this.el.slideIn(this.getSlideAnchor(), {
44809             callback: function(){
44810                 this.afterSlide();
44811                 this.initAutoHide();
44812                 Roo.get(document).on("click", this.slideInIf, this);
44813                 this.fireEvent("slideshow", this);
44814             },
44815             scope: this,
44816             block: true
44817         });
44818     },
44819
44820     afterSlideIn : function(){
44821         this.clearAutoHide();
44822         this.isSlid = false;
44823         this.clearMonitor();
44824         this.el.setStyle("z-index", "");
44825         if(this.collapseBtn){
44826             this.collapseBtn.show();
44827         }
44828         this.closeBtn.setStyle('display', this.closeBtnState);
44829         if(this.stickBtn){
44830             this.stickBtn.hide();
44831         }
44832         this.fireEvent("slidehide", this);
44833     },
44834
44835     slideIn : function(cb){
44836         if(!this.isSlid || this.el.hasActiveFx()){
44837             Roo.callback(cb);
44838             return;
44839         }
44840         this.isSlid = false;
44841         this.beforeSlide();
44842         this.el.slideOut(this.getSlideAnchor(), {
44843             callback: function(){
44844                 this.el.setLeftTop(-10000, -10000);
44845                 this.afterSlide();
44846                 this.afterSlideIn();
44847                 Roo.callback(cb);
44848             },
44849             scope: this,
44850             block: true
44851         });
44852     },
44853     
44854     slideInIf : function(e){
44855         if(!e.within(this.el)){
44856             this.slideIn();
44857         }
44858     },
44859
44860     animateCollapse : function(){
44861         this.beforeSlide();
44862         this.el.setStyle("z-index", 20000);
44863         var anchor = this.getSlideAnchor();
44864         this.el.slideOut(anchor, {
44865             callback : function(){
44866                 this.el.setStyle("z-index", "");
44867                 this.collapsedEl.slideIn(anchor, {duration:.3});
44868                 this.afterSlide();
44869                 this.el.setLocation(-10000,-10000);
44870                 this.el.hide();
44871                 this.fireEvent("collapsed", this);
44872             },
44873             scope: this,
44874             block: true
44875         });
44876     },
44877
44878     animateExpand : function(){
44879         this.beforeSlide();
44880         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44881         this.el.setStyle("z-index", 20000);
44882         this.collapsedEl.hide({
44883             duration:.1
44884         });
44885         this.el.slideIn(this.getSlideAnchor(), {
44886             callback : function(){
44887                 this.el.setStyle("z-index", "");
44888                 this.afterSlide();
44889                 if(this.split){
44890                     this.split.el.show();
44891                 }
44892                 this.fireEvent("invalidated", this);
44893                 this.fireEvent("expanded", this);
44894             },
44895             scope: this,
44896             block: true
44897         });
44898     },
44899
44900     anchors : {
44901         "west" : "left",
44902         "east" : "right",
44903         "north" : "top",
44904         "south" : "bottom"
44905     },
44906
44907     sanchors : {
44908         "west" : "l",
44909         "east" : "r",
44910         "north" : "t",
44911         "south" : "b"
44912     },
44913
44914     canchors : {
44915         "west" : "tl-tr",
44916         "east" : "tr-tl",
44917         "north" : "tl-bl",
44918         "south" : "bl-tl"
44919     },
44920
44921     getAnchor : function(){
44922         return this.anchors[this.position];
44923     },
44924
44925     getCollapseAnchor : function(){
44926         return this.canchors[this.position];
44927     },
44928
44929     getSlideAnchor : function(){
44930         return this.sanchors[this.position];
44931     },
44932
44933     getAlignAdj : function(){
44934         var cm = this.cmargins;
44935         switch(this.position){
44936             case "west":
44937                 return [0, 0];
44938             break;
44939             case "east":
44940                 return [0, 0];
44941             break;
44942             case "north":
44943                 return [0, 0];
44944             break;
44945             case "south":
44946                 return [0, 0];
44947             break;
44948         }
44949     },
44950
44951     getExpandAdj : function(){
44952         var c = this.collapsedEl, cm = this.cmargins;
44953         switch(this.position){
44954             case "west":
44955                 return [-(cm.right+c.getWidth()+cm.left), 0];
44956             break;
44957             case "east":
44958                 return [cm.right+c.getWidth()+cm.left, 0];
44959             break;
44960             case "north":
44961                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44962             break;
44963             case "south":
44964                 return [0, cm.top+cm.bottom+c.getHeight()];
44965             break;
44966         }
44967     }
44968 });/*
44969  * Based on:
44970  * Ext JS Library 1.1.1
44971  * Copyright(c) 2006-2007, Ext JS, LLC.
44972  *
44973  * Originally Released Under LGPL - original licence link has changed is not relivant.
44974  *
44975  * Fork - LGPL
44976  * <script type="text/javascript">
44977  */
44978 /*
44979  * These classes are private internal classes
44980  */
44981 Roo.bootstrap.layout.Center = function(config){
44982     config.region = "center";
44983     Roo.bootstrap.layout.Region.call(this, config);
44984     this.visible = true;
44985     this.minWidth = config.minWidth || 20;
44986     this.minHeight = config.minHeight || 20;
44987 };
44988
44989 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44990     hide : function(){
44991         // center panel can't be hidden
44992     },
44993     
44994     show : function(){
44995         // center panel can't be hidden
44996     },
44997     
44998     getMinWidth: function(){
44999         return this.minWidth;
45000     },
45001     
45002     getMinHeight: function(){
45003         return this.minHeight;
45004     }
45005 });
45006
45007
45008
45009
45010  
45011
45012
45013
45014
45015
45016
45017 Roo.bootstrap.layout.North = function(config)
45018 {
45019     config.region = 'north';
45020     config.cursor = 'n-resize';
45021     
45022     Roo.bootstrap.layout.Split.call(this, config);
45023     
45024     
45025     if(this.split){
45026         this.split.placement = Roo.bootstrap.SplitBar.TOP;
45027         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45028         this.split.el.addClass("roo-layout-split-v");
45029     }
45030     //var size = config.initialSize || config.height;
45031     //if(this.el && typeof size != "undefined"){
45032     //    this.el.setHeight(size);
45033     //}
45034 };
45035 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45036 {
45037     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45038      
45039      
45040     onRender : function(ctr, pos)
45041     {
45042         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45043         var size = this.config.initialSize || this.config.height;
45044         if(this.el && typeof size != "undefined"){
45045             this.el.setHeight(size);
45046         }
45047     
45048     },
45049     
45050     getBox : function(){
45051         if(this.collapsed){
45052             return this.collapsedEl.getBox();
45053         }
45054         var box = this.el.getBox();
45055         if(this.split){
45056             box.height += this.split.el.getHeight();
45057         }
45058         return box;
45059     },
45060     
45061     updateBox : function(box){
45062         if(this.split && !this.collapsed){
45063             box.height -= this.split.el.getHeight();
45064             this.split.el.setLeft(box.x);
45065             this.split.el.setTop(box.y+box.height);
45066             this.split.el.setWidth(box.width);
45067         }
45068         if(this.collapsed){
45069             this.updateBody(box.width, null);
45070         }
45071         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45072     }
45073 });
45074
45075
45076
45077
45078
45079 Roo.bootstrap.layout.South = function(config){
45080     config.region = 'south';
45081     config.cursor = 's-resize';
45082     Roo.bootstrap.layout.Split.call(this, config);
45083     if(this.split){
45084         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45085         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45086         this.split.el.addClass("roo-layout-split-v");
45087     }
45088     
45089 };
45090
45091 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45092     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45093     
45094     onRender : function(ctr, pos)
45095     {
45096         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45097         var size = this.config.initialSize || this.config.height;
45098         if(this.el && typeof size != "undefined"){
45099             this.el.setHeight(size);
45100         }
45101     
45102     },
45103     
45104     getBox : function(){
45105         if(this.collapsed){
45106             return this.collapsedEl.getBox();
45107         }
45108         var box = this.el.getBox();
45109         if(this.split){
45110             var sh = this.split.el.getHeight();
45111             box.height += sh;
45112             box.y -= sh;
45113         }
45114         return box;
45115     },
45116     
45117     updateBox : function(box){
45118         if(this.split && !this.collapsed){
45119             var sh = this.split.el.getHeight();
45120             box.height -= sh;
45121             box.y += sh;
45122             this.split.el.setLeft(box.x);
45123             this.split.el.setTop(box.y-sh);
45124             this.split.el.setWidth(box.width);
45125         }
45126         if(this.collapsed){
45127             this.updateBody(box.width, null);
45128         }
45129         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45130     }
45131 });
45132
45133 Roo.bootstrap.layout.East = function(config){
45134     config.region = "east";
45135     config.cursor = "e-resize";
45136     Roo.bootstrap.layout.Split.call(this, config);
45137     if(this.split){
45138         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45139         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45140         this.split.el.addClass("roo-layout-split-h");
45141     }
45142     
45143 };
45144 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45145     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45146     
45147     onRender : function(ctr, pos)
45148     {
45149         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45150         var size = this.config.initialSize || this.config.width;
45151         if(this.el && typeof size != "undefined"){
45152             this.el.setWidth(size);
45153         }
45154     
45155     },
45156     
45157     getBox : function(){
45158         if(this.collapsed){
45159             return this.collapsedEl.getBox();
45160         }
45161         var box = this.el.getBox();
45162         if(this.split){
45163             var sw = this.split.el.getWidth();
45164             box.width += sw;
45165             box.x -= sw;
45166         }
45167         return box;
45168     },
45169
45170     updateBox : function(box){
45171         if(this.split && !this.collapsed){
45172             var sw = this.split.el.getWidth();
45173             box.width -= sw;
45174             this.split.el.setLeft(box.x);
45175             this.split.el.setTop(box.y);
45176             this.split.el.setHeight(box.height);
45177             box.x += sw;
45178         }
45179         if(this.collapsed){
45180             this.updateBody(null, box.height);
45181         }
45182         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45183     }
45184 });
45185
45186 Roo.bootstrap.layout.West = function(config){
45187     config.region = "west";
45188     config.cursor = "w-resize";
45189     
45190     Roo.bootstrap.layout.Split.call(this, config);
45191     if(this.split){
45192         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45193         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45194         this.split.el.addClass("roo-layout-split-h");
45195     }
45196     
45197 };
45198 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45199     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45200     
45201     onRender: function(ctr, pos)
45202     {
45203         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45204         var size = this.config.initialSize || this.config.width;
45205         if(typeof size != "undefined"){
45206             this.el.setWidth(size);
45207         }
45208     },
45209     
45210     getBox : function(){
45211         if(this.collapsed){
45212             return this.collapsedEl.getBox();
45213         }
45214         var box = this.el.getBox();
45215         if (box.width == 0) {
45216             box.width = this.config.width; // kludge?
45217         }
45218         if(this.split){
45219             box.width += this.split.el.getWidth();
45220         }
45221         return box;
45222     },
45223     
45224     updateBox : function(box){
45225         if(this.split && !this.collapsed){
45226             var sw = this.split.el.getWidth();
45227             box.width -= sw;
45228             this.split.el.setLeft(box.x+box.width);
45229             this.split.el.setTop(box.y);
45230             this.split.el.setHeight(box.height);
45231         }
45232         if(this.collapsed){
45233             this.updateBody(null, box.height);
45234         }
45235         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45236     }
45237 });/*
45238  * Based on:
45239  * Ext JS Library 1.1.1
45240  * Copyright(c) 2006-2007, Ext JS, LLC.
45241  *
45242  * Originally Released Under LGPL - original licence link has changed is not relivant.
45243  *
45244  * Fork - LGPL
45245  * <script type="text/javascript">
45246  */
45247 /**
45248  * @class Roo.bootstrap.paenl.Content
45249  * @extends Roo.util.Observable
45250  * @children Roo.bootstrap.Component
45251  * @parent builder Roo.bootstrap.layout.Border
45252  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45253  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45254  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45255  * @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
45256  * @cfg {Boolean}   closable      True if the panel can be closed/removed
45257  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
45258  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45259  * @cfg {Toolbar}   toolbar       A toolbar for this panel
45260  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
45261  * @cfg {String} title          The title for this panel
45262  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45263  * @cfg {String} url            Calls {@link #setUrl} with this value
45264  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45265  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
45266  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
45267  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
45268  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
45269  * @cfg {Boolean} badges render the badges
45270  * @cfg {String} cls  extra classes to use  
45271  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45272  
45273  * @constructor
45274  * Create a new ContentPanel.
45275  * @param {String/Object} config A string to set only the title or a config object
45276  
45277  */
45278 Roo.bootstrap.panel.Content = function( config){
45279     
45280     this.tpl = config.tpl || false;
45281     
45282     var el = config.el;
45283     var content = config.content;
45284
45285     if(config.autoCreate){ // xtype is available if this is called from factory
45286         el = Roo.id();
45287     }
45288     this.el = Roo.get(el);
45289     if(!this.el && config && config.autoCreate){
45290         if(typeof config.autoCreate == "object"){
45291             if(!config.autoCreate.id){
45292                 config.autoCreate.id = config.id||el;
45293             }
45294             this.el = Roo.DomHelper.append(document.body,
45295                         config.autoCreate, true);
45296         }else{
45297             var elcfg =  {
45298                 tag: "div",
45299                 cls: (config.cls || '') +
45300                     (config.background ? ' bg-' + config.background : '') +
45301                     " roo-layout-inactive-content",
45302                 id: config.id||el
45303             };
45304             if (config.iframe) {
45305                 elcfg.cn = [
45306                     {
45307                         tag : 'iframe',
45308                         style : 'border: 0px',
45309                         src : 'about:blank'
45310                     }
45311                 ];
45312             }
45313               
45314             if (config.html) {
45315                 elcfg.html = config.html;
45316                 
45317             }
45318                         
45319             this.el = Roo.DomHelper.append(document.body, elcfg , true);
45320             if (config.iframe) {
45321                 this.iframeEl = this.el.select('iframe',true).first();
45322             }
45323             
45324         }
45325     } 
45326     this.closable = false;
45327     this.loaded = false;
45328     this.active = false;
45329    
45330       
45331     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45332         
45333         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45334         
45335         this.wrapEl = this.el; //this.el.wrap();
45336         var ti = [];
45337         if (config.toolbar.items) {
45338             ti = config.toolbar.items ;
45339             delete config.toolbar.items ;
45340         }
45341         
45342         var nitems = [];
45343         this.toolbar.render(this.wrapEl, 'before');
45344         for(var i =0;i < ti.length;i++) {
45345           //  Roo.log(['add child', items[i]]);
45346             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45347         }
45348         this.toolbar.items = nitems;
45349         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45350         delete config.toolbar;
45351         
45352     }
45353     /*
45354     // xtype created footer. - not sure if will work as we normally have to render first..
45355     if (this.footer && !this.footer.el && this.footer.xtype) {
45356         if (!this.wrapEl) {
45357             this.wrapEl = this.el.wrap();
45358         }
45359     
45360         this.footer.container = this.wrapEl.createChild();
45361          
45362         this.footer = Roo.factory(this.footer, Roo);
45363         
45364     }
45365     */
45366     
45367      if(typeof config == "string"){
45368         this.title = config;
45369     }else{
45370         Roo.apply(this, config);
45371     }
45372     
45373     if(this.resizeEl){
45374         this.resizeEl = Roo.get(this.resizeEl, true);
45375     }else{
45376         this.resizeEl = this.el;
45377     }
45378     // handle view.xtype
45379     
45380  
45381     
45382     
45383     this.addEvents({
45384         /**
45385          * @event activate
45386          * Fires when this panel is activated. 
45387          * @param {Roo.ContentPanel} this
45388          */
45389         "activate" : true,
45390         /**
45391          * @event deactivate
45392          * Fires when this panel is activated. 
45393          * @param {Roo.ContentPanel} this
45394          */
45395         "deactivate" : true,
45396
45397         /**
45398          * @event resize
45399          * Fires when this panel is resized if fitToFrame is true.
45400          * @param {Roo.ContentPanel} this
45401          * @param {Number} width The width after any component adjustments
45402          * @param {Number} height The height after any component adjustments
45403          */
45404         "resize" : true,
45405         
45406          /**
45407          * @event render
45408          * Fires when this tab is created
45409          * @param {Roo.ContentPanel} this
45410          */
45411         "render" : true,
45412         
45413           /**
45414          * @event scroll
45415          * Fires when this content is scrolled
45416          * @param {Roo.ContentPanel} this
45417          * @param {Event} scrollEvent
45418          */
45419         "scroll" : true
45420         
45421         
45422         
45423     });
45424     
45425
45426     
45427     
45428     if(this.autoScroll && !this.iframe){
45429         this.resizeEl.setStyle("overflow", "auto");
45430         this.resizeEl.on('scroll', this.onScroll, this);
45431     } else {
45432         // fix randome scrolling
45433         //this.el.on('scroll', function() {
45434         //    Roo.log('fix random scolling');
45435         //    this.scrollTo('top',0); 
45436         //});
45437     }
45438     content = content || this.content;
45439     if(content){
45440         this.setContent(content);
45441     }
45442     if(config && config.url){
45443         this.setUrl(this.url, this.params, this.loadOnce);
45444     }
45445     
45446     
45447     
45448     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45449     
45450     if (this.view && typeof(this.view.xtype) != 'undefined') {
45451         this.view.el = this.el.appendChild(document.createElement("div"));
45452         this.view = Roo.factory(this.view); 
45453         this.view.render  &&  this.view.render(false, '');  
45454     }
45455     
45456     
45457     this.fireEvent('render', this);
45458 };
45459
45460 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45461     
45462     cls : '',
45463     background : '',
45464     
45465     tabTip : '',
45466     
45467     iframe : false,
45468     iframeEl : false,
45469     
45470     /* Resize Element - use this to work out scroll etc. */
45471     resizeEl : false,
45472     
45473     setRegion : function(region){
45474         this.region = region;
45475         this.setActiveClass(region && !this.background);
45476     },
45477     
45478     
45479     setActiveClass: function(state)
45480     {
45481         if(state){
45482            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45483            this.el.setStyle('position','relative');
45484         }else{
45485            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45486            this.el.setStyle('position', 'absolute');
45487         } 
45488     },
45489     
45490     /**
45491      * Returns the toolbar for this Panel if one was configured. 
45492      * @return {Roo.Toolbar} 
45493      */
45494     getToolbar : function(){
45495         return this.toolbar;
45496     },
45497     
45498     setActiveState : function(active)
45499     {
45500         this.active = active;
45501         this.setActiveClass(active);
45502         if(!active){
45503             if(this.fireEvent("deactivate", this) === false){
45504                 return false;
45505             }
45506             return true;
45507         }
45508         this.fireEvent("activate", this);
45509         return true;
45510     },
45511     /**
45512      * Updates this panel's element (not for iframe)
45513      * @param {String} content The new content
45514      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45515     */
45516     setContent : function(content, loadScripts){
45517         if (this.iframe) {
45518             return;
45519         }
45520         
45521         this.el.update(content, loadScripts);
45522     },
45523
45524     ignoreResize : function(w, h)
45525     {
45526         //return false; // always resize?
45527         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45528             return true;
45529         }else{
45530             this.lastSize = {width: w, height: h};
45531             return false;
45532         }
45533     },
45534     /**
45535      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45536      * @return {Roo.UpdateManager} The UpdateManager
45537      */
45538     getUpdateManager : function(){
45539         if (this.iframe) {
45540             return false;
45541         }
45542         return this.el.getUpdateManager();
45543     },
45544      /**
45545      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45546      * Does not work with IFRAME contents
45547      * @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:
45548 <pre><code>
45549 panel.load({
45550     url: "your-url.php",
45551     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45552     callback: yourFunction,
45553     scope: yourObject, //(optional scope)
45554     discardUrl: false,
45555     nocache: false,
45556     text: "Loading...",
45557     timeout: 30,
45558     scripts: false
45559 });
45560 </code></pre>
45561      
45562      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45563      * 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.
45564      * @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}
45565      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45566      * @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.
45567      * @return {Roo.ContentPanel} this
45568      */
45569     load : function(){
45570         
45571         if (this.iframe) {
45572             return this;
45573         }
45574         
45575         var um = this.el.getUpdateManager();
45576         um.update.apply(um, arguments);
45577         return this;
45578     },
45579
45580
45581     /**
45582      * 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.
45583      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45584      * @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)
45585      * @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)
45586      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45587      */
45588     setUrl : function(url, params, loadOnce){
45589         if (this.iframe) {
45590             this.iframeEl.dom.src = url;
45591             return false;
45592         }
45593         
45594         if(this.refreshDelegate){
45595             this.removeListener("activate", this.refreshDelegate);
45596         }
45597         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45598         this.on("activate", this.refreshDelegate);
45599         return this.el.getUpdateManager();
45600     },
45601     
45602     _handleRefresh : function(url, params, loadOnce){
45603         if(!loadOnce || !this.loaded){
45604             var updater = this.el.getUpdateManager();
45605             updater.update(url, params, this._setLoaded.createDelegate(this));
45606         }
45607     },
45608     
45609     _setLoaded : function(){
45610         this.loaded = true;
45611     }, 
45612     
45613     /**
45614      * Returns this panel's id
45615      * @return {String} 
45616      */
45617     getId : function(){
45618         return this.el.id;
45619     },
45620     
45621     /** 
45622      * Returns this panel's element - used by regiosn to add.
45623      * @return {Roo.Element} 
45624      */
45625     getEl : function(){
45626         return this.wrapEl || this.el;
45627     },
45628     
45629    
45630     
45631     adjustForComponents : function(width, height)
45632     {
45633         //Roo.log('adjustForComponents ');
45634         if(this.resizeEl != this.el){
45635             width -= this.el.getFrameWidth('lr');
45636             height -= this.el.getFrameWidth('tb');
45637         }
45638         if(this.toolbar){
45639             var te = this.toolbar.getEl();
45640             te.setWidth(width);
45641             height -= te.getHeight();
45642         }
45643         if(this.footer){
45644             var te = this.footer.getEl();
45645             te.setWidth(width);
45646             height -= te.getHeight();
45647         }
45648         
45649         
45650         if(this.adjustments){
45651             width += this.adjustments[0];
45652             height += this.adjustments[1];
45653         }
45654         return {"width": width, "height": height};
45655     },
45656     
45657     setSize : function(width, height){
45658         if(this.fitToFrame && !this.ignoreResize(width, height)){
45659             if(this.fitContainer && this.resizeEl != this.el){
45660                 this.el.setSize(width, height);
45661             }
45662             var size = this.adjustForComponents(width, height);
45663             if (this.iframe) {
45664                 this.iframeEl.setSize(width,height);
45665             }
45666             
45667             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45668             this.fireEvent('resize', this, size.width, size.height);
45669             
45670             
45671         }
45672     },
45673     
45674     /**
45675      * Returns this panel's title
45676      * @return {String} 
45677      */
45678     getTitle : function(){
45679         
45680         if (typeof(this.title) != 'object') {
45681             return this.title;
45682         }
45683         
45684         var t = '';
45685         for (var k in this.title) {
45686             if (!this.title.hasOwnProperty(k)) {
45687                 continue;
45688             }
45689             
45690             if (k.indexOf('-') >= 0) {
45691                 var s = k.split('-');
45692                 for (var i = 0; i<s.length; i++) {
45693                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45694                 }
45695             } else {
45696                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45697             }
45698         }
45699         return t;
45700     },
45701     
45702     /**
45703      * Set this panel's title
45704      * @param {String} title
45705      */
45706     setTitle : function(title){
45707         this.title = title;
45708         if(this.region){
45709             this.region.updatePanelTitle(this, title);
45710         }
45711     },
45712     
45713     /**
45714      * Returns true is this panel was configured to be closable
45715      * @return {Boolean} 
45716      */
45717     isClosable : function(){
45718         return this.closable;
45719     },
45720     
45721     beforeSlide : function(){
45722         this.el.clip();
45723         this.resizeEl.clip();
45724     },
45725     
45726     afterSlide : function(){
45727         this.el.unclip();
45728         this.resizeEl.unclip();
45729     },
45730     
45731     /**
45732      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45733      *   Will fail silently if the {@link #setUrl} method has not been called.
45734      *   This does not activate the panel, just updates its content.
45735      */
45736     refresh : function(){
45737         if(this.refreshDelegate){
45738            this.loaded = false;
45739            this.refreshDelegate();
45740         }
45741     },
45742     
45743     /**
45744      * Destroys this panel
45745      */
45746     destroy : function(){
45747         this.el.removeAllListeners();
45748         var tempEl = document.createElement("span");
45749         tempEl.appendChild(this.el.dom);
45750         tempEl.innerHTML = "";
45751         this.el.remove();
45752         this.el = null;
45753     },
45754     
45755     /**
45756      * form - if the content panel contains a form - this is a reference to it.
45757      * @type {Roo.form.Form}
45758      */
45759     form : false,
45760     /**
45761      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45762      *    This contains a reference to it.
45763      * @type {Roo.View}
45764      */
45765     view : false,
45766     
45767       /**
45768      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45769      * <pre><code>
45770
45771 layout.addxtype({
45772        xtype : 'Form',
45773        items: [ .... ]
45774    }
45775 );
45776
45777 </code></pre>
45778      * @param {Object} cfg Xtype definition of item to add.
45779      */
45780     
45781     
45782     getChildContainer: function () {
45783         return this.getEl();
45784     },
45785     
45786     
45787     onScroll : function(e)
45788     {
45789         this.fireEvent('scroll', this, e);
45790     }
45791     
45792     
45793     /*
45794         var  ret = new Roo.factory(cfg);
45795         return ret;
45796         
45797         
45798         // add form..
45799         if (cfg.xtype.match(/^Form$/)) {
45800             
45801             var el;
45802             //if (this.footer) {
45803             //    el = this.footer.container.insertSibling(false, 'before');
45804             //} else {
45805                 el = this.el.createChild();
45806             //}
45807
45808             this.form = new  Roo.form.Form(cfg);
45809             
45810             
45811             if ( this.form.allItems.length) {
45812                 this.form.render(el.dom);
45813             }
45814             return this.form;
45815         }
45816         // should only have one of theses..
45817         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45818             // views.. should not be just added - used named prop 'view''
45819             
45820             cfg.el = this.el.appendChild(document.createElement("div"));
45821             // factory?
45822             
45823             var ret = new Roo.factory(cfg);
45824              
45825              ret.render && ret.render(false, ''); // render blank..
45826             this.view = ret;
45827             return ret;
45828         }
45829         return false;
45830     }
45831     \*/
45832 });
45833  
45834 /**
45835  * @class Roo.bootstrap.panel.Grid
45836  * @extends Roo.bootstrap.panel.Content
45837  * @constructor
45838  * Create a new GridPanel.
45839  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45840  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45841  * @param {Object} config A the config object
45842   
45843  */
45844
45845
45846
45847 Roo.bootstrap.panel.Grid = function(config)
45848 {
45849     
45850       
45851     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45852         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45853
45854     config.el = this.wrapper;
45855     //this.el = this.wrapper;
45856     
45857       if (config.container) {
45858         // ctor'ed from a Border/panel.grid
45859         
45860         
45861         this.wrapper.setStyle("overflow", "hidden");
45862         this.wrapper.addClass('roo-grid-container');
45863
45864     }
45865     
45866     
45867     if(config.toolbar){
45868         var tool_el = this.wrapper.createChild();    
45869         this.toolbar = Roo.factory(config.toolbar);
45870         var ti = [];
45871         if (config.toolbar.items) {
45872             ti = config.toolbar.items ;
45873             delete config.toolbar.items ;
45874         }
45875         
45876         var nitems = [];
45877         this.toolbar.render(tool_el);
45878         for(var i =0;i < ti.length;i++) {
45879           //  Roo.log(['add child', items[i]]);
45880             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45881         }
45882         this.toolbar.items = nitems;
45883         
45884         delete config.toolbar;
45885     }
45886     
45887     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45888     config.grid.scrollBody = true;;
45889     config.grid.monitorWindowResize = false; // turn off autosizing
45890     config.grid.autoHeight = false;
45891     config.grid.autoWidth = false;
45892     
45893     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45894     
45895     if (config.background) {
45896         // render grid on panel activation (if panel background)
45897         this.on('activate', function(gp) {
45898             if (!gp.grid.rendered) {
45899                 gp.grid.render(this.wrapper);
45900                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45901             }
45902         });
45903             
45904     } else {
45905         this.grid.render(this.wrapper);
45906         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45907
45908     }
45909     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45910     // ??? needed ??? config.el = this.wrapper;
45911     
45912     
45913     
45914   
45915     // xtype created footer. - not sure if will work as we normally have to render first..
45916     if (this.footer && !this.footer.el && this.footer.xtype) {
45917         
45918         var ctr = this.grid.getView().getFooterPanel(true);
45919         this.footer.dataSource = this.grid.dataSource;
45920         this.footer = Roo.factory(this.footer, Roo);
45921         this.footer.render(ctr);
45922         
45923     }
45924     
45925     
45926     
45927     
45928      
45929 };
45930
45931 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45932 {
45933   
45934     getId : function(){
45935         return this.grid.id;
45936     },
45937     
45938     /**
45939      * Returns the grid for this panel
45940      * @return {Roo.bootstrap.Table} 
45941      */
45942     getGrid : function(){
45943         return this.grid;    
45944     },
45945     
45946     setSize : function(width, height)
45947     {
45948      
45949         //if(!this.ignoreResize(width, height)){
45950             var grid = this.grid;
45951             var size = this.adjustForComponents(width, height);
45952             // tfoot is not a footer?
45953           
45954             
45955             var gridel = grid.getGridEl();
45956             gridel.setSize(size.width, size.height);
45957             
45958             var tbd = grid.getGridEl().select('tbody', true).first();
45959             var thd = grid.getGridEl().select('thead',true).first();
45960             var tbf= grid.getGridEl().select('tfoot', true).first();
45961
45962             if (tbf) {
45963                 size.height -= tbf.getHeight();
45964             }
45965             if (thd) {
45966                 size.height -= thd.getHeight();
45967             }
45968             
45969             tbd.setSize(size.width, size.height );
45970             // this is for the account management tab -seems to work there.
45971             var thd = grid.getGridEl().select('thead',true).first();
45972             //if (tbd) {
45973             //    tbd.setSize(size.width, size.height - thd.getHeight());
45974             //}
45975              
45976             grid.autoSize();
45977         //}
45978    
45979     },
45980      
45981     
45982     
45983     beforeSlide : function(){
45984         this.grid.getView().scroller.clip();
45985     },
45986     
45987     afterSlide : function(){
45988         this.grid.getView().scroller.unclip();
45989     },
45990     
45991     destroy : function(){
45992         this.grid.destroy();
45993         delete this.grid;
45994         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45995     }
45996 });
45997
45998 /**
45999  * @class Roo.bootstrap.panel.Nest
46000  * @extends Roo.bootstrap.panel.Content
46001  * @constructor
46002  * Create a new Panel, that can contain a layout.Border.
46003  * 
46004  * 
46005  * @param {String/Object} config A string to set only the title or a config object
46006  */
46007 Roo.bootstrap.panel.Nest = function(config)
46008 {
46009     // construct with only one argument..
46010     /* FIXME - implement nicer consturctors
46011     if (layout.layout) {
46012         config = layout;
46013         layout = config.layout;
46014         delete config.layout;
46015     }
46016     if (layout.xtype && !layout.getEl) {
46017         // then layout needs constructing..
46018         layout = Roo.factory(layout, Roo);
46019     }
46020     */
46021     
46022     config.el =  config.layout.getEl();
46023     
46024     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46025     
46026     config.layout.monitorWindowResize = false; // turn off autosizing
46027     this.layout = config.layout;
46028     this.layout.getEl().addClass("roo-layout-nested-layout");
46029     this.layout.parent = this;
46030     
46031     
46032     
46033     
46034 };
46035
46036 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46037     /**
46038     * @cfg {Roo.BorderLayout} layout The layout for this panel
46039     */
46040     layout : false,
46041
46042     setSize : function(width, height){
46043         if(!this.ignoreResize(width, height)){
46044             var size = this.adjustForComponents(width, height);
46045             var el = this.layout.getEl();
46046             if (size.height < 1) {
46047                 el.setWidth(size.width);   
46048             } else {
46049                 el.setSize(size.width, size.height);
46050             }
46051             var touch = el.dom.offsetWidth;
46052             this.layout.layout();
46053             // ie requires a double layout on the first pass
46054             if(Roo.isIE && !this.initialized){
46055                 this.initialized = true;
46056                 this.layout.layout();
46057             }
46058         }
46059     },
46060     
46061     // activate all subpanels if not currently active..
46062     
46063     setActiveState : function(active){
46064         this.active = active;
46065         this.setActiveClass(active);
46066         
46067         if(!active){
46068             this.fireEvent("deactivate", this);
46069             return;
46070         }
46071         
46072         this.fireEvent("activate", this);
46073         // not sure if this should happen before or after..
46074         if (!this.layout) {
46075             return; // should not happen..
46076         }
46077         var reg = false;
46078         for (var r in this.layout.regions) {
46079             reg = this.layout.getRegion(r);
46080             if (reg.getActivePanel()) {
46081                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46082                 reg.setActivePanel(reg.getActivePanel());
46083                 continue;
46084             }
46085             if (!reg.panels.length) {
46086                 continue;
46087             }
46088             reg.showPanel(reg.getPanel(0));
46089         }
46090         
46091         
46092         
46093         
46094     },
46095     
46096     /**
46097      * Returns the nested BorderLayout for this panel
46098      * @return {Roo.BorderLayout} 
46099      */
46100     getLayout : function(){
46101         return this.layout;
46102     },
46103     
46104      /**
46105      * Adds a xtype elements to the layout of the nested panel
46106      * <pre><code>
46107
46108 panel.addxtype({
46109        xtype : 'ContentPanel',
46110        region: 'west',
46111        items: [ .... ]
46112    }
46113 );
46114
46115 panel.addxtype({
46116         xtype : 'NestedLayoutPanel',
46117         region: 'west',
46118         layout: {
46119            center: { },
46120            west: { }   
46121         },
46122         items : [ ... list of content panels or nested layout panels.. ]
46123    }
46124 );
46125 </code></pre>
46126      * @param {Object} cfg Xtype definition of item to add.
46127      */
46128     addxtype : function(cfg) {
46129         return this.layout.addxtype(cfg);
46130     
46131     }
46132 });/*
46133  * Based on:
46134  * Ext JS Library 1.1.1
46135  * Copyright(c) 2006-2007, Ext JS, LLC.
46136  *
46137  * Originally Released Under LGPL - original licence link has changed is not relivant.
46138  *
46139  * Fork - LGPL
46140  * <script type="text/javascript">
46141  */
46142 /**
46143  * @class Roo.TabPanel
46144  * @extends Roo.util.Observable
46145  * A lightweight tab container.
46146  * <br><br>
46147  * Usage:
46148  * <pre><code>
46149 // basic tabs 1, built from existing content
46150 var tabs = new Roo.TabPanel("tabs1");
46151 tabs.addTab("script", "View Script");
46152 tabs.addTab("markup", "View Markup");
46153 tabs.activate("script");
46154
46155 // more advanced tabs, built from javascript
46156 var jtabs = new Roo.TabPanel("jtabs");
46157 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46158
46159 // set up the UpdateManager
46160 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46161 var updater = tab2.getUpdateManager();
46162 updater.setDefaultUrl("ajax1.htm");
46163 tab2.on('activate', updater.refresh, updater, true);
46164
46165 // Use setUrl for Ajax loading
46166 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46167 tab3.setUrl("ajax2.htm", null, true);
46168
46169 // Disabled tab
46170 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46171 tab4.disable();
46172
46173 jtabs.activate("jtabs-1");
46174  * </code></pre>
46175  * @constructor
46176  * Create a new TabPanel.
46177  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46178  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46179  */
46180 Roo.bootstrap.panel.Tabs = function(config){
46181     /**
46182     * The container element for this TabPanel.
46183     * @type Roo.Element
46184     */
46185     this.el = Roo.get(config.el);
46186     delete config.el;
46187     if(config){
46188         if(typeof config == "boolean"){
46189             this.tabPosition = config ? "bottom" : "top";
46190         }else{
46191             Roo.apply(this, config);
46192         }
46193     }
46194     
46195     if(this.tabPosition == "bottom"){
46196         // if tabs are at the bottom = create the body first.
46197         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46198         this.el.addClass("roo-tabs-bottom");
46199     }
46200     // next create the tabs holders
46201     
46202     if (this.tabPosition == "west"){
46203         
46204         var reg = this.region; // fake it..
46205         while (reg) {
46206             if (!reg.mgr.parent) {
46207                 break;
46208             }
46209             reg = reg.mgr.parent.region;
46210         }
46211         Roo.log("got nest?");
46212         Roo.log(reg);
46213         if (reg.mgr.getRegion('west')) {
46214             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46215             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46216             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46217             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46218             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46219         
46220             
46221         }
46222         
46223         
46224     } else {
46225      
46226         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46227         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46228         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46229         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46230     }
46231     
46232     
46233     if(Roo.isIE){
46234         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46235     }
46236     
46237     // finally - if tabs are at the top, then create the body last..
46238     if(this.tabPosition != "bottom"){
46239         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46240          * @type Roo.Element
46241          */
46242         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46243         this.el.addClass("roo-tabs-top");
46244     }
46245     this.items = [];
46246
46247     this.bodyEl.setStyle("position", "relative");
46248
46249     this.active = null;
46250     this.activateDelegate = this.activate.createDelegate(this);
46251
46252     this.addEvents({
46253         /**
46254          * @event tabchange
46255          * Fires when the active tab changes
46256          * @param {Roo.TabPanel} this
46257          * @param {Roo.TabPanelItem} activePanel The new active tab
46258          */
46259         "tabchange": true,
46260         /**
46261          * @event beforetabchange
46262          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46263          * @param {Roo.TabPanel} this
46264          * @param {Object} e Set cancel to true on this object to cancel the tab change
46265          * @param {Roo.TabPanelItem} tab The tab being changed to
46266          */
46267         "beforetabchange" : true
46268     });
46269
46270     Roo.EventManager.onWindowResize(this.onResize, this);
46271     this.cpad = this.el.getPadding("lr");
46272     this.hiddenCount = 0;
46273
46274
46275     // toolbar on the tabbar support...
46276     if (this.toolbar) {
46277         alert("no toolbar support yet");
46278         this.toolbar  = false;
46279         /*
46280         var tcfg = this.toolbar;
46281         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
46282         this.toolbar = new Roo.Toolbar(tcfg);
46283         if (Roo.isSafari) {
46284             var tbl = tcfg.container.child('table', true);
46285             tbl.setAttribute('width', '100%');
46286         }
46287         */
46288         
46289     }
46290    
46291
46292
46293     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46294 };
46295
46296 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46297     /*
46298      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46299      */
46300     tabPosition : "top",
46301     /*
46302      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46303      */
46304     currentTabWidth : 0,
46305     /*
46306      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46307      */
46308     minTabWidth : 40,
46309     /*
46310      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46311      */
46312     maxTabWidth : 250,
46313     /*
46314      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46315      */
46316     preferredTabWidth : 175,
46317     /*
46318      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46319      */
46320     resizeTabs : false,
46321     /*
46322      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46323      */
46324     monitorResize : true,
46325     /*
46326      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
46327      */
46328     toolbar : false,  // set by caller..
46329     
46330     region : false, /// set by caller
46331     
46332     disableTooltips : true, // not used yet...
46333
46334     /**
46335      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46336      * @param {String} id The id of the div to use <b>or create</b>
46337      * @param {String} text The text for the tab
46338      * @param {String} content (optional) Content to put in the TabPanelItem body
46339      * @param {Boolean} closable (optional) True to create a close icon on the tab
46340      * @return {Roo.TabPanelItem} The created TabPanelItem
46341      */
46342     addTab : function(id, text, content, closable, tpl)
46343     {
46344         var item = new Roo.bootstrap.panel.TabItem({
46345             panel: this,
46346             id : id,
46347             text : text,
46348             closable : closable,
46349             tpl : tpl
46350         });
46351         this.addTabItem(item);
46352         if(content){
46353             item.setContent(content);
46354         }
46355         return item;
46356     },
46357
46358     /**
46359      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46360      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46361      * @return {Roo.TabPanelItem}
46362      */
46363     getTab : function(id){
46364         return this.items[id];
46365     },
46366
46367     /**
46368      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46369      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46370      */
46371     hideTab : function(id){
46372         var t = this.items[id];
46373         if(!t.isHidden()){
46374            t.setHidden(true);
46375            this.hiddenCount++;
46376            this.autoSizeTabs();
46377         }
46378     },
46379
46380     /**
46381      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46382      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46383      */
46384     unhideTab : function(id){
46385         var t = this.items[id];
46386         if(t.isHidden()){
46387            t.setHidden(false);
46388            this.hiddenCount--;
46389            this.autoSizeTabs();
46390         }
46391     },
46392
46393     /**
46394      * Adds an existing {@link Roo.TabPanelItem}.
46395      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46396      */
46397     addTabItem : function(item)
46398     {
46399         this.items[item.id] = item;
46400         this.items.push(item);
46401         this.autoSizeTabs();
46402       //  if(this.resizeTabs){
46403     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46404   //         this.autoSizeTabs();
46405 //        }else{
46406 //            item.autoSize();
46407        // }
46408     },
46409
46410     /**
46411      * Removes a {@link Roo.TabPanelItem}.
46412      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46413      */
46414     removeTab : function(id){
46415         var items = this.items;
46416         var tab = items[id];
46417         if(!tab) { return; }
46418         var index = items.indexOf(tab);
46419         if(this.active == tab && items.length > 1){
46420             var newTab = this.getNextAvailable(index);
46421             if(newTab) {
46422                 newTab.activate();
46423             }
46424         }
46425         this.stripEl.dom.removeChild(tab.pnode.dom);
46426         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46427             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46428         }
46429         items.splice(index, 1);
46430         delete this.items[tab.id];
46431         tab.fireEvent("close", tab);
46432         tab.purgeListeners();
46433         this.autoSizeTabs();
46434     },
46435
46436     getNextAvailable : function(start){
46437         var items = this.items;
46438         var index = start;
46439         // look for a next tab that will slide over to
46440         // replace the one being removed
46441         while(index < items.length){
46442             var item = items[++index];
46443             if(item && !item.isHidden()){
46444                 return item;
46445             }
46446         }
46447         // if one isn't found select the previous tab (on the left)
46448         index = start;
46449         while(index >= 0){
46450             var item = items[--index];
46451             if(item && !item.isHidden()){
46452                 return item;
46453             }
46454         }
46455         return null;
46456     },
46457
46458     /**
46459      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46460      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46461      */
46462     disableTab : function(id){
46463         var tab = this.items[id];
46464         if(tab && this.active != tab){
46465             tab.disable();
46466         }
46467     },
46468
46469     /**
46470      * Enables a {@link Roo.TabPanelItem} that is disabled.
46471      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46472      */
46473     enableTab : function(id){
46474         var tab = this.items[id];
46475         tab.enable();
46476     },
46477
46478     /**
46479      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46480      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46481      * @return {Roo.TabPanelItem} The TabPanelItem.
46482      */
46483     activate : function(id)
46484     {
46485         //Roo.log('activite:'  + id);
46486         
46487         var tab = this.items[id];
46488         if(!tab){
46489             return null;
46490         }
46491         if(tab == this.active || tab.disabled){
46492             return tab;
46493         }
46494         var e = {};
46495         this.fireEvent("beforetabchange", this, e, tab);
46496         if(e.cancel !== true && !tab.disabled){
46497             if(this.active){
46498                 this.active.hide();
46499             }
46500             this.active = this.items[id];
46501             this.active.show();
46502             this.fireEvent("tabchange", this, this.active);
46503         }
46504         return tab;
46505     },
46506
46507     /**
46508      * Gets the active {@link Roo.TabPanelItem}.
46509      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46510      */
46511     getActiveTab : function(){
46512         return this.active;
46513     },
46514
46515     /**
46516      * Updates the tab body element to fit the height of the container element
46517      * for overflow scrolling
46518      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46519      */
46520     syncHeight : function(targetHeight){
46521         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46522         var bm = this.bodyEl.getMargins();
46523         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46524         this.bodyEl.setHeight(newHeight);
46525         return newHeight;
46526     },
46527
46528     onResize : function(){
46529         if(this.monitorResize){
46530             this.autoSizeTabs();
46531         }
46532     },
46533
46534     /**
46535      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46536      */
46537     beginUpdate : function(){
46538         this.updating = true;
46539     },
46540
46541     /**
46542      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46543      */
46544     endUpdate : function(){
46545         this.updating = false;
46546         this.autoSizeTabs();
46547     },
46548
46549     /**
46550      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46551      */
46552     autoSizeTabs : function()
46553     {
46554         var count = this.items.length;
46555         var vcount = count - this.hiddenCount;
46556         
46557         if (vcount < 2) {
46558             this.stripEl.hide();
46559         } else {
46560             this.stripEl.show();
46561         }
46562         
46563         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46564             return;
46565         }
46566         
46567         
46568         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46569         var availWidth = Math.floor(w / vcount);
46570         var b = this.stripBody;
46571         if(b.getWidth() > w){
46572             var tabs = this.items;
46573             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46574             if(availWidth < this.minTabWidth){
46575                 /*if(!this.sleft){    // incomplete scrolling code
46576                     this.createScrollButtons();
46577                 }
46578                 this.showScroll();
46579                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46580             }
46581         }else{
46582             if(this.currentTabWidth < this.preferredTabWidth){
46583                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46584             }
46585         }
46586     },
46587
46588     /**
46589      * Returns the number of tabs in this TabPanel.
46590      * @return {Number}
46591      */
46592      getCount : function(){
46593          return this.items.length;
46594      },
46595
46596     /**
46597      * Resizes all the tabs to the passed width
46598      * @param {Number} The new width
46599      */
46600     setTabWidth : function(width){
46601         this.currentTabWidth = width;
46602         for(var i = 0, len = this.items.length; i < len; i++) {
46603                 if(!this.items[i].isHidden()) {
46604                 this.items[i].setWidth(width);
46605             }
46606         }
46607     },
46608
46609     /**
46610      * Destroys this TabPanel
46611      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46612      */
46613     destroy : function(removeEl){
46614         Roo.EventManager.removeResizeListener(this.onResize, this);
46615         for(var i = 0, len = this.items.length; i < len; i++){
46616             this.items[i].purgeListeners();
46617         }
46618         if(removeEl === true){
46619             this.el.update("");
46620             this.el.remove();
46621         }
46622     },
46623     
46624     createStrip : function(container)
46625     {
46626         var strip = document.createElement("nav");
46627         strip.className = Roo.bootstrap.version == 4 ?
46628             "navbar-light bg-light" : 
46629             "navbar navbar-default"; //"x-tabs-wrap";
46630         container.appendChild(strip);
46631         return strip;
46632     },
46633     
46634     createStripList : function(strip)
46635     {
46636         // div wrapper for retard IE
46637         // returns the "tr" element.
46638         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46639         //'<div class="x-tabs-strip-wrap">'+
46640           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46641           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46642         return strip.firstChild; //.firstChild.firstChild.firstChild;
46643     },
46644     createBody : function(container)
46645     {
46646         var body = document.createElement("div");
46647         Roo.id(body, "tab-body");
46648         //Roo.fly(body).addClass("x-tabs-body");
46649         Roo.fly(body).addClass("tab-content");
46650         container.appendChild(body);
46651         return body;
46652     },
46653     createItemBody :function(bodyEl, id){
46654         var body = Roo.getDom(id);
46655         if(!body){
46656             body = document.createElement("div");
46657             body.id = id;
46658         }
46659         //Roo.fly(body).addClass("x-tabs-item-body");
46660         Roo.fly(body).addClass("tab-pane");
46661          bodyEl.insertBefore(body, bodyEl.firstChild);
46662         return body;
46663     },
46664     /** @private */
46665     createStripElements :  function(stripEl, text, closable, tpl)
46666     {
46667         var td = document.createElement("li"); // was td..
46668         td.className = 'nav-item';
46669         
46670         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46671         
46672         
46673         stripEl.appendChild(td);
46674         /*if(closable){
46675             td.className = "x-tabs-closable";
46676             if(!this.closeTpl){
46677                 this.closeTpl = new Roo.Template(
46678                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46679                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46680                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
46681                 );
46682             }
46683             var el = this.closeTpl.overwrite(td, {"text": text});
46684             var close = el.getElementsByTagName("div")[0];
46685             var inner = el.getElementsByTagName("em")[0];
46686             return {"el": el, "close": close, "inner": inner};
46687         } else {
46688         */
46689         // not sure what this is..
46690 //            if(!this.tabTpl){
46691                 //this.tabTpl = new Roo.Template(
46692                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46693                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46694                 //);
46695 //                this.tabTpl = new Roo.Template(
46696 //                   '<a href="#">' +
46697 //                   '<span unselectable="on"' +
46698 //                            (this.disableTooltips ? '' : ' title="{text}"') +
46699 //                            ' >{text}</span></a>'
46700 //                );
46701 //                
46702 //            }
46703
46704
46705             var template = tpl || this.tabTpl || false;
46706             
46707             if(!template){
46708                 template =  new Roo.Template(
46709                         Roo.bootstrap.version == 4 ? 
46710                             (
46711                                 '<a class="nav-link" href="#" unselectable="on"' +
46712                                      (this.disableTooltips ? '' : ' title="{text}"') +
46713                                      ' >{text}</a>'
46714                             ) : (
46715                                 '<a class="nav-link" href="#">' +
46716                                 '<span unselectable="on"' +
46717                                          (this.disableTooltips ? '' : ' title="{text}"') +
46718                                     ' >{text}</span></a>'
46719                             )
46720                 );
46721             }
46722             
46723             switch (typeof(template)) {
46724                 case 'object' :
46725                     break;
46726                 case 'string' :
46727                     template = new Roo.Template(template);
46728                     break;
46729                 default :
46730                     break;
46731             }
46732             
46733             var el = template.overwrite(td, {"text": text});
46734             
46735             var inner = el.getElementsByTagName("span")[0];
46736             
46737             return {"el": el, "inner": inner};
46738             
46739     }
46740         
46741     
46742 });
46743
46744 /**
46745  * @class Roo.TabPanelItem
46746  * @extends Roo.util.Observable
46747  * Represents an individual item (tab plus body) in a TabPanel.
46748  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46749  * @param {String} id The id of this TabPanelItem
46750  * @param {String} text The text for the tab of this TabPanelItem
46751  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46752  */
46753 Roo.bootstrap.panel.TabItem = function(config){
46754     /**
46755      * The {@link Roo.TabPanel} this TabPanelItem belongs to
46756      * @type Roo.TabPanel
46757      */
46758     this.tabPanel = config.panel;
46759     /**
46760      * The id for this TabPanelItem
46761      * @type String
46762      */
46763     this.id = config.id;
46764     /** @private */
46765     this.disabled = false;
46766     /** @private */
46767     this.text = config.text;
46768     /** @private */
46769     this.loaded = false;
46770     this.closable = config.closable;
46771
46772     /**
46773      * The body element for this TabPanelItem.
46774      * @type Roo.Element
46775      */
46776     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46777     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46778     this.bodyEl.setStyle("display", "block");
46779     this.bodyEl.setStyle("zoom", "1");
46780     //this.hideAction();
46781
46782     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46783     /** @private */
46784     this.el = Roo.get(els.el);
46785     this.inner = Roo.get(els.inner, true);
46786      this.textEl = Roo.bootstrap.version == 4 ?
46787         this.el : Roo.get(this.el.dom.firstChild, true);
46788
46789     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46790     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46791
46792     
46793 //    this.el.on("mousedown", this.onTabMouseDown, this);
46794     this.el.on("click", this.onTabClick, this);
46795     /** @private */
46796     if(config.closable){
46797         var c = Roo.get(els.close, true);
46798         c.dom.title = this.closeText;
46799         c.addClassOnOver("close-over");
46800         c.on("click", this.closeClick, this);
46801      }
46802
46803     this.addEvents({
46804          /**
46805          * @event activate
46806          * Fires when this tab becomes the active tab.
46807          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46808          * @param {Roo.TabPanelItem} this
46809          */
46810         "activate": true,
46811         /**
46812          * @event beforeclose
46813          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46814          * @param {Roo.TabPanelItem} this
46815          * @param {Object} e Set cancel to true on this object to cancel the close.
46816          */
46817         "beforeclose": true,
46818         /**
46819          * @event close
46820          * Fires when this tab is closed.
46821          * @param {Roo.TabPanelItem} this
46822          */
46823          "close": true,
46824         /**
46825          * @event deactivate
46826          * Fires when this tab is no longer the active tab.
46827          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46828          * @param {Roo.TabPanelItem} this
46829          */
46830          "deactivate" : true
46831     });
46832     this.hidden = false;
46833
46834     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46835 };
46836
46837 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46838            {
46839     purgeListeners : function(){
46840        Roo.util.Observable.prototype.purgeListeners.call(this);
46841        this.el.removeAllListeners();
46842     },
46843     /**
46844      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46845      */
46846     show : function(){
46847         this.status_node.addClass("active");
46848         this.showAction();
46849         if(Roo.isOpera){
46850             this.tabPanel.stripWrap.repaint();
46851         }
46852         this.fireEvent("activate", this.tabPanel, this);
46853     },
46854
46855     /**
46856      * Returns true if this tab is the active tab.
46857      * @return {Boolean}
46858      */
46859     isActive : function(){
46860         return this.tabPanel.getActiveTab() == this;
46861     },
46862
46863     /**
46864      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46865      */
46866     hide : function(){
46867         this.status_node.removeClass("active");
46868         this.hideAction();
46869         this.fireEvent("deactivate", this.tabPanel, this);
46870     },
46871
46872     hideAction : function(){
46873         this.bodyEl.hide();
46874         this.bodyEl.setStyle("position", "absolute");
46875         this.bodyEl.setLeft("-20000px");
46876         this.bodyEl.setTop("-20000px");
46877     },
46878
46879     showAction : function(){
46880         this.bodyEl.setStyle("position", "relative");
46881         this.bodyEl.setTop("");
46882         this.bodyEl.setLeft("");
46883         this.bodyEl.show();
46884     },
46885
46886     /**
46887      * Set the tooltip for the tab.
46888      * @param {String} tooltip The tab's tooltip
46889      */
46890     setTooltip : function(text){
46891         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46892             this.textEl.dom.qtip = text;
46893             this.textEl.dom.removeAttribute('title');
46894         }else{
46895             this.textEl.dom.title = text;
46896         }
46897     },
46898
46899     onTabClick : function(e){
46900         e.preventDefault();
46901         this.tabPanel.activate(this.id);
46902     },
46903
46904     onTabMouseDown : function(e){
46905         e.preventDefault();
46906         this.tabPanel.activate(this.id);
46907     },
46908 /*
46909     getWidth : function(){
46910         return this.inner.getWidth();
46911     },
46912
46913     setWidth : function(width){
46914         var iwidth = width - this.linode.getPadding("lr");
46915         this.inner.setWidth(iwidth);
46916         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46917         this.linode.setWidth(width);
46918     },
46919 */
46920     /**
46921      * Show or hide the tab
46922      * @param {Boolean} hidden True to hide or false to show.
46923      */
46924     setHidden : function(hidden){
46925         this.hidden = hidden;
46926         this.linode.setStyle("display", hidden ? "none" : "");
46927     },
46928
46929     /**
46930      * Returns true if this tab is "hidden"
46931      * @return {Boolean}
46932      */
46933     isHidden : function(){
46934         return this.hidden;
46935     },
46936
46937     /**
46938      * Returns the text for this tab
46939      * @return {String}
46940      */
46941     getText : function(){
46942         return this.text;
46943     },
46944     /*
46945     autoSize : function(){
46946         //this.el.beginMeasure();
46947         this.textEl.setWidth(1);
46948         /*
46949          *  #2804 [new] Tabs in Roojs
46950          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46951          */
46952         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46953         //this.el.endMeasure();
46954     //},
46955
46956     /**
46957      * Sets the text for the tab (Note: this also sets the tooltip text)
46958      * @param {String} text The tab's text and tooltip
46959      */
46960     setText : function(text){
46961         this.text = text;
46962         this.textEl.update(text);
46963         this.setTooltip(text);
46964         //if(!this.tabPanel.resizeTabs){
46965         //    this.autoSize();
46966         //}
46967     },
46968     /**
46969      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46970      */
46971     activate : function(){
46972         this.tabPanel.activate(this.id);
46973     },
46974
46975     /**
46976      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46977      */
46978     disable : function(){
46979         if(this.tabPanel.active != this){
46980             this.disabled = true;
46981             this.status_node.addClass("disabled");
46982         }
46983     },
46984
46985     /**
46986      * Enables this TabPanelItem if it was previously disabled.
46987      */
46988     enable : function(){
46989         this.disabled = false;
46990         this.status_node.removeClass("disabled");
46991     },
46992
46993     /**
46994      * Sets the content for this TabPanelItem.
46995      * @param {String} content The content
46996      * @param {Boolean} loadScripts true to look for and load scripts
46997      */
46998     setContent : function(content, loadScripts){
46999         this.bodyEl.update(content, loadScripts);
47000     },
47001
47002     /**
47003      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47004      * @return {Roo.UpdateManager} The UpdateManager
47005      */
47006     getUpdateManager : function(){
47007         return this.bodyEl.getUpdateManager();
47008     },
47009
47010     /**
47011      * Set a URL to be used to load the content for this TabPanelItem.
47012      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47013      * @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)
47014      * @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)
47015      * @return {Roo.UpdateManager} The UpdateManager
47016      */
47017     setUrl : function(url, params, loadOnce){
47018         if(this.refreshDelegate){
47019             this.un('activate', this.refreshDelegate);
47020         }
47021         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47022         this.on("activate", this.refreshDelegate);
47023         return this.bodyEl.getUpdateManager();
47024     },
47025
47026     /** @private */
47027     _handleRefresh : function(url, params, loadOnce){
47028         if(!loadOnce || !this.loaded){
47029             var updater = this.bodyEl.getUpdateManager();
47030             updater.update(url, params, this._setLoaded.createDelegate(this));
47031         }
47032     },
47033
47034     /**
47035      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
47036      *   Will fail silently if the setUrl method has not been called.
47037      *   This does not activate the panel, just updates its content.
47038      */
47039     refresh : function(){
47040         if(this.refreshDelegate){
47041            this.loaded = false;
47042            this.refreshDelegate();
47043         }
47044     },
47045
47046     /** @private */
47047     _setLoaded : function(){
47048         this.loaded = true;
47049     },
47050
47051     /** @private */
47052     closeClick : function(e){
47053         var o = {};
47054         e.stopEvent();
47055         this.fireEvent("beforeclose", this, o);
47056         if(o.cancel !== true){
47057             this.tabPanel.removeTab(this.id);
47058         }
47059     },
47060     /**
47061      * The text displayed in the tooltip for the close icon.
47062      * @type String
47063      */
47064     closeText : "Close this tab"
47065 });
47066 /**
47067 *    This script refer to:
47068 *    Title: International Telephone Input
47069 *    Author: Jack O'Connor
47070 *    Code version:  v12.1.12
47071 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47072 **/
47073
47074 Roo.bootstrap.form.PhoneInputData = function() {
47075     var d = [
47076       [
47077         "Afghanistan (‫افغانستان‬‎)",
47078         "af",
47079         "93"
47080       ],
47081       [
47082         "Albania (Shqipëri)",
47083         "al",
47084         "355"
47085       ],
47086       [
47087         "Algeria (‫الجزائر‬‎)",
47088         "dz",
47089         "213"
47090       ],
47091       [
47092         "American Samoa",
47093         "as",
47094         "1684"
47095       ],
47096       [
47097         "Andorra",
47098         "ad",
47099         "376"
47100       ],
47101       [
47102         "Angola",
47103         "ao",
47104         "244"
47105       ],
47106       [
47107         "Anguilla",
47108         "ai",
47109         "1264"
47110       ],
47111       [
47112         "Antigua and Barbuda",
47113         "ag",
47114         "1268"
47115       ],
47116       [
47117         "Argentina",
47118         "ar",
47119         "54"
47120       ],
47121       [
47122         "Armenia (Հայաստան)",
47123         "am",
47124         "374"
47125       ],
47126       [
47127         "Aruba",
47128         "aw",
47129         "297"
47130       ],
47131       [
47132         "Australia",
47133         "au",
47134         "61",
47135         0
47136       ],
47137       [
47138         "Austria (Österreich)",
47139         "at",
47140         "43"
47141       ],
47142       [
47143         "Azerbaijan (Azərbaycan)",
47144         "az",
47145         "994"
47146       ],
47147       [
47148         "Bahamas",
47149         "bs",
47150         "1242"
47151       ],
47152       [
47153         "Bahrain (‫البحرين‬‎)",
47154         "bh",
47155         "973"
47156       ],
47157       [
47158         "Bangladesh (বাংলাদেশ)",
47159         "bd",
47160         "880"
47161       ],
47162       [
47163         "Barbados",
47164         "bb",
47165         "1246"
47166       ],
47167       [
47168         "Belarus (Беларусь)",
47169         "by",
47170         "375"
47171       ],
47172       [
47173         "Belgium (België)",
47174         "be",
47175         "32"
47176       ],
47177       [
47178         "Belize",
47179         "bz",
47180         "501"
47181       ],
47182       [
47183         "Benin (Bénin)",
47184         "bj",
47185         "229"
47186       ],
47187       [
47188         "Bermuda",
47189         "bm",
47190         "1441"
47191       ],
47192       [
47193         "Bhutan (འབྲུག)",
47194         "bt",
47195         "975"
47196       ],
47197       [
47198         "Bolivia",
47199         "bo",
47200         "591"
47201       ],
47202       [
47203         "Bosnia and Herzegovina (Босна и Херцеговина)",
47204         "ba",
47205         "387"
47206       ],
47207       [
47208         "Botswana",
47209         "bw",
47210         "267"
47211       ],
47212       [
47213         "Brazil (Brasil)",
47214         "br",
47215         "55"
47216       ],
47217       [
47218         "British Indian Ocean Territory",
47219         "io",
47220         "246"
47221       ],
47222       [
47223         "British Virgin Islands",
47224         "vg",
47225         "1284"
47226       ],
47227       [
47228         "Brunei",
47229         "bn",
47230         "673"
47231       ],
47232       [
47233         "Bulgaria (България)",
47234         "bg",
47235         "359"
47236       ],
47237       [
47238         "Burkina Faso",
47239         "bf",
47240         "226"
47241       ],
47242       [
47243         "Burundi (Uburundi)",
47244         "bi",
47245         "257"
47246       ],
47247       [
47248         "Cambodia (កម្ពុជា)",
47249         "kh",
47250         "855"
47251       ],
47252       [
47253         "Cameroon (Cameroun)",
47254         "cm",
47255         "237"
47256       ],
47257       [
47258         "Canada",
47259         "ca",
47260         "1",
47261         1,
47262         ["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"]
47263       ],
47264       [
47265         "Cape Verde (Kabu Verdi)",
47266         "cv",
47267         "238"
47268       ],
47269       [
47270         "Caribbean Netherlands",
47271         "bq",
47272         "599",
47273         1
47274       ],
47275       [
47276         "Cayman Islands",
47277         "ky",
47278         "1345"
47279       ],
47280       [
47281         "Central African Republic (République centrafricaine)",
47282         "cf",
47283         "236"
47284       ],
47285       [
47286         "Chad (Tchad)",
47287         "td",
47288         "235"
47289       ],
47290       [
47291         "Chile",
47292         "cl",
47293         "56"
47294       ],
47295       [
47296         "China (中国)",
47297         "cn",
47298         "86"
47299       ],
47300       [
47301         "Christmas Island",
47302         "cx",
47303         "61",
47304         2
47305       ],
47306       [
47307         "Cocos (Keeling) Islands",
47308         "cc",
47309         "61",
47310         1
47311       ],
47312       [
47313         "Colombia",
47314         "co",
47315         "57"
47316       ],
47317       [
47318         "Comoros (‫جزر القمر‬‎)",
47319         "km",
47320         "269"
47321       ],
47322       [
47323         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47324         "cd",
47325         "243"
47326       ],
47327       [
47328         "Congo (Republic) (Congo-Brazzaville)",
47329         "cg",
47330         "242"
47331       ],
47332       [
47333         "Cook Islands",
47334         "ck",
47335         "682"
47336       ],
47337       [
47338         "Costa Rica",
47339         "cr",
47340         "506"
47341       ],
47342       [
47343         "Côte d’Ivoire",
47344         "ci",
47345         "225"
47346       ],
47347       [
47348         "Croatia (Hrvatska)",
47349         "hr",
47350         "385"
47351       ],
47352       [
47353         "Cuba",
47354         "cu",
47355         "53"
47356       ],
47357       [
47358         "Curaçao",
47359         "cw",
47360         "599",
47361         0
47362       ],
47363       [
47364         "Cyprus (Κύπρος)",
47365         "cy",
47366         "357"
47367       ],
47368       [
47369         "Czech Republic (Česká republika)",
47370         "cz",
47371         "420"
47372       ],
47373       [
47374         "Denmark (Danmark)",
47375         "dk",
47376         "45"
47377       ],
47378       [
47379         "Djibouti",
47380         "dj",
47381         "253"
47382       ],
47383       [
47384         "Dominica",
47385         "dm",
47386         "1767"
47387       ],
47388       [
47389         "Dominican Republic (República Dominicana)",
47390         "do",
47391         "1",
47392         2,
47393         ["809", "829", "849"]
47394       ],
47395       [
47396         "Ecuador",
47397         "ec",
47398         "593"
47399       ],
47400       [
47401         "Egypt (‫مصر‬‎)",
47402         "eg",
47403         "20"
47404       ],
47405       [
47406         "El Salvador",
47407         "sv",
47408         "503"
47409       ],
47410       [
47411         "Equatorial Guinea (Guinea Ecuatorial)",
47412         "gq",
47413         "240"
47414       ],
47415       [
47416         "Eritrea",
47417         "er",
47418         "291"
47419       ],
47420       [
47421         "Estonia (Eesti)",
47422         "ee",
47423         "372"
47424       ],
47425       [
47426         "Ethiopia",
47427         "et",
47428         "251"
47429       ],
47430       [
47431         "Falkland Islands (Islas Malvinas)",
47432         "fk",
47433         "500"
47434       ],
47435       [
47436         "Faroe Islands (Føroyar)",
47437         "fo",
47438         "298"
47439       ],
47440       [
47441         "Fiji",
47442         "fj",
47443         "679"
47444       ],
47445       [
47446         "Finland (Suomi)",
47447         "fi",
47448         "358",
47449         0
47450       ],
47451       [
47452         "France",
47453         "fr",
47454         "33"
47455       ],
47456       [
47457         "French Guiana (Guyane française)",
47458         "gf",
47459         "594"
47460       ],
47461       [
47462         "French Polynesia (Polynésie française)",
47463         "pf",
47464         "689"
47465       ],
47466       [
47467         "Gabon",
47468         "ga",
47469         "241"
47470       ],
47471       [
47472         "Gambia",
47473         "gm",
47474         "220"
47475       ],
47476       [
47477         "Georgia (საქართველო)",
47478         "ge",
47479         "995"
47480       ],
47481       [
47482         "Germany (Deutschland)",
47483         "de",
47484         "49"
47485       ],
47486       [
47487         "Ghana (Gaana)",
47488         "gh",
47489         "233"
47490       ],
47491       [
47492         "Gibraltar",
47493         "gi",
47494         "350"
47495       ],
47496       [
47497         "Greece (Ελλάδα)",
47498         "gr",
47499         "30"
47500       ],
47501       [
47502         "Greenland (Kalaallit Nunaat)",
47503         "gl",
47504         "299"
47505       ],
47506       [
47507         "Grenada",
47508         "gd",
47509         "1473"
47510       ],
47511       [
47512         "Guadeloupe",
47513         "gp",
47514         "590",
47515         0
47516       ],
47517       [
47518         "Guam",
47519         "gu",
47520         "1671"
47521       ],
47522       [
47523         "Guatemala",
47524         "gt",
47525         "502"
47526       ],
47527       [
47528         "Guernsey",
47529         "gg",
47530         "44",
47531         1
47532       ],
47533       [
47534         "Guinea (Guinée)",
47535         "gn",
47536         "224"
47537       ],
47538       [
47539         "Guinea-Bissau (Guiné Bissau)",
47540         "gw",
47541         "245"
47542       ],
47543       [
47544         "Guyana",
47545         "gy",
47546         "592"
47547       ],
47548       [
47549         "Haiti",
47550         "ht",
47551         "509"
47552       ],
47553       [
47554         "Honduras",
47555         "hn",
47556         "504"
47557       ],
47558       [
47559         "Hong Kong (香港)",
47560         "hk",
47561         "852"
47562       ],
47563       [
47564         "Hungary (Magyarország)",
47565         "hu",
47566         "36"
47567       ],
47568       [
47569         "Iceland (Ísland)",
47570         "is",
47571         "354"
47572       ],
47573       [
47574         "India (भारत)",
47575         "in",
47576         "91"
47577       ],
47578       [
47579         "Indonesia",
47580         "id",
47581         "62"
47582       ],
47583       [
47584         "Iran (‫ایران‬‎)",
47585         "ir",
47586         "98"
47587       ],
47588       [
47589         "Iraq (‫العراق‬‎)",
47590         "iq",
47591         "964"
47592       ],
47593       [
47594         "Ireland",
47595         "ie",
47596         "353"
47597       ],
47598       [
47599         "Isle of Man",
47600         "im",
47601         "44",
47602         2
47603       ],
47604       [
47605         "Israel (‫ישראל‬‎)",
47606         "il",
47607         "972"
47608       ],
47609       [
47610         "Italy (Italia)",
47611         "it",
47612         "39",
47613         0
47614       ],
47615       [
47616         "Jamaica",
47617         "jm",
47618         "1876"
47619       ],
47620       [
47621         "Japan (日本)",
47622         "jp",
47623         "81"
47624       ],
47625       [
47626         "Jersey",
47627         "je",
47628         "44",
47629         3
47630       ],
47631       [
47632         "Jordan (‫الأردن‬‎)",
47633         "jo",
47634         "962"
47635       ],
47636       [
47637         "Kazakhstan (Казахстан)",
47638         "kz",
47639         "7",
47640         1
47641       ],
47642       [
47643         "Kenya",
47644         "ke",
47645         "254"
47646       ],
47647       [
47648         "Kiribati",
47649         "ki",
47650         "686"
47651       ],
47652       [
47653         "Kosovo",
47654         "xk",
47655         "383"
47656       ],
47657       [
47658         "Kuwait (‫الكويت‬‎)",
47659         "kw",
47660         "965"
47661       ],
47662       [
47663         "Kyrgyzstan (Кыргызстан)",
47664         "kg",
47665         "996"
47666       ],
47667       [
47668         "Laos (ລາວ)",
47669         "la",
47670         "856"
47671       ],
47672       [
47673         "Latvia (Latvija)",
47674         "lv",
47675         "371"
47676       ],
47677       [
47678         "Lebanon (‫لبنان‬‎)",
47679         "lb",
47680         "961"
47681       ],
47682       [
47683         "Lesotho",
47684         "ls",
47685         "266"
47686       ],
47687       [
47688         "Liberia",
47689         "lr",
47690         "231"
47691       ],
47692       [
47693         "Libya (‫ليبيا‬‎)",
47694         "ly",
47695         "218"
47696       ],
47697       [
47698         "Liechtenstein",
47699         "li",
47700         "423"
47701       ],
47702       [
47703         "Lithuania (Lietuva)",
47704         "lt",
47705         "370"
47706       ],
47707       [
47708         "Luxembourg",
47709         "lu",
47710         "352"
47711       ],
47712       [
47713         "Macau (澳門)",
47714         "mo",
47715         "853"
47716       ],
47717       [
47718         "Macedonia (FYROM) (Македонија)",
47719         "mk",
47720         "389"
47721       ],
47722       [
47723         "Madagascar (Madagasikara)",
47724         "mg",
47725         "261"
47726       ],
47727       [
47728         "Malawi",
47729         "mw",
47730         "265"
47731       ],
47732       [
47733         "Malaysia",
47734         "my",
47735         "60"
47736       ],
47737       [
47738         "Maldives",
47739         "mv",
47740         "960"
47741       ],
47742       [
47743         "Mali",
47744         "ml",
47745         "223"
47746       ],
47747       [
47748         "Malta",
47749         "mt",
47750         "356"
47751       ],
47752       [
47753         "Marshall Islands",
47754         "mh",
47755         "692"
47756       ],
47757       [
47758         "Martinique",
47759         "mq",
47760         "596"
47761       ],
47762       [
47763         "Mauritania (‫موريتانيا‬‎)",
47764         "mr",
47765         "222"
47766       ],
47767       [
47768         "Mauritius (Moris)",
47769         "mu",
47770         "230"
47771       ],
47772       [
47773         "Mayotte",
47774         "yt",
47775         "262",
47776         1
47777       ],
47778       [
47779         "Mexico (México)",
47780         "mx",
47781         "52"
47782       ],
47783       [
47784         "Micronesia",
47785         "fm",
47786         "691"
47787       ],
47788       [
47789         "Moldova (Republica Moldova)",
47790         "md",
47791         "373"
47792       ],
47793       [
47794         "Monaco",
47795         "mc",
47796         "377"
47797       ],
47798       [
47799         "Mongolia (Монгол)",
47800         "mn",
47801         "976"
47802       ],
47803       [
47804         "Montenegro (Crna Gora)",
47805         "me",
47806         "382"
47807       ],
47808       [
47809         "Montserrat",
47810         "ms",
47811         "1664"
47812       ],
47813       [
47814         "Morocco (‫المغرب‬‎)",
47815         "ma",
47816         "212",
47817         0
47818       ],
47819       [
47820         "Mozambique (Moçambique)",
47821         "mz",
47822         "258"
47823       ],
47824       [
47825         "Myanmar (Burma) (မြန်မာ)",
47826         "mm",
47827         "95"
47828       ],
47829       [
47830         "Namibia (Namibië)",
47831         "na",
47832         "264"
47833       ],
47834       [
47835         "Nauru",
47836         "nr",
47837         "674"
47838       ],
47839       [
47840         "Nepal (नेपाल)",
47841         "np",
47842         "977"
47843       ],
47844       [
47845         "Netherlands (Nederland)",
47846         "nl",
47847         "31"
47848       ],
47849       [
47850         "New Caledonia (Nouvelle-Calédonie)",
47851         "nc",
47852         "687"
47853       ],
47854       [
47855         "New Zealand",
47856         "nz",
47857         "64"
47858       ],
47859       [
47860         "Nicaragua",
47861         "ni",
47862         "505"
47863       ],
47864       [
47865         "Niger (Nijar)",
47866         "ne",
47867         "227"
47868       ],
47869       [
47870         "Nigeria",
47871         "ng",
47872         "234"
47873       ],
47874       [
47875         "Niue",
47876         "nu",
47877         "683"
47878       ],
47879       [
47880         "Norfolk Island",
47881         "nf",
47882         "672"
47883       ],
47884       [
47885         "North Korea (조선 민주주의 인민 공화국)",
47886         "kp",
47887         "850"
47888       ],
47889       [
47890         "Northern Mariana Islands",
47891         "mp",
47892         "1670"
47893       ],
47894       [
47895         "Norway (Norge)",
47896         "no",
47897         "47",
47898         0
47899       ],
47900       [
47901         "Oman (‫عُمان‬‎)",
47902         "om",
47903         "968"
47904       ],
47905       [
47906         "Pakistan (‫پاکستان‬‎)",
47907         "pk",
47908         "92"
47909       ],
47910       [
47911         "Palau",
47912         "pw",
47913         "680"
47914       ],
47915       [
47916         "Palestine (‫فلسطين‬‎)",
47917         "ps",
47918         "970"
47919       ],
47920       [
47921         "Panama (Panamá)",
47922         "pa",
47923         "507"
47924       ],
47925       [
47926         "Papua New Guinea",
47927         "pg",
47928         "675"
47929       ],
47930       [
47931         "Paraguay",
47932         "py",
47933         "595"
47934       ],
47935       [
47936         "Peru (Perú)",
47937         "pe",
47938         "51"
47939       ],
47940       [
47941         "Philippines",
47942         "ph",
47943         "63"
47944       ],
47945       [
47946         "Poland (Polska)",
47947         "pl",
47948         "48"
47949       ],
47950       [
47951         "Portugal",
47952         "pt",
47953         "351"
47954       ],
47955       [
47956         "Puerto Rico",
47957         "pr",
47958         "1",
47959         3,
47960         ["787", "939"]
47961       ],
47962       [
47963         "Qatar (‫قطر‬‎)",
47964         "qa",
47965         "974"
47966       ],
47967       [
47968         "Réunion (La Réunion)",
47969         "re",
47970         "262",
47971         0
47972       ],
47973       [
47974         "Romania (România)",
47975         "ro",
47976         "40"
47977       ],
47978       [
47979         "Russia (Россия)",
47980         "ru",
47981         "7",
47982         0
47983       ],
47984       [
47985         "Rwanda",
47986         "rw",
47987         "250"
47988       ],
47989       [
47990         "Saint Barthélemy",
47991         "bl",
47992         "590",
47993         1
47994       ],
47995       [
47996         "Saint Helena",
47997         "sh",
47998         "290"
47999       ],
48000       [
48001         "Saint Kitts and Nevis",
48002         "kn",
48003         "1869"
48004       ],
48005       [
48006         "Saint Lucia",
48007         "lc",
48008         "1758"
48009       ],
48010       [
48011         "Saint Martin (Saint-Martin (partie française))",
48012         "mf",
48013         "590",
48014         2
48015       ],
48016       [
48017         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48018         "pm",
48019         "508"
48020       ],
48021       [
48022         "Saint Vincent and the Grenadines",
48023         "vc",
48024         "1784"
48025       ],
48026       [
48027         "Samoa",
48028         "ws",
48029         "685"
48030       ],
48031       [
48032         "San Marino",
48033         "sm",
48034         "378"
48035       ],
48036       [
48037         "São Tomé and Príncipe (São Tomé e Príncipe)",
48038         "st",
48039         "239"
48040       ],
48041       [
48042         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
48043         "sa",
48044         "966"
48045       ],
48046       [
48047         "Senegal (Sénégal)",
48048         "sn",
48049         "221"
48050       ],
48051       [
48052         "Serbia (Србија)",
48053         "rs",
48054         "381"
48055       ],
48056       [
48057         "Seychelles",
48058         "sc",
48059         "248"
48060       ],
48061       [
48062         "Sierra Leone",
48063         "sl",
48064         "232"
48065       ],
48066       [
48067         "Singapore",
48068         "sg",
48069         "65"
48070       ],
48071       [
48072         "Sint Maarten",
48073         "sx",
48074         "1721"
48075       ],
48076       [
48077         "Slovakia (Slovensko)",
48078         "sk",
48079         "421"
48080       ],
48081       [
48082         "Slovenia (Slovenija)",
48083         "si",
48084         "386"
48085       ],
48086       [
48087         "Solomon Islands",
48088         "sb",
48089         "677"
48090       ],
48091       [
48092         "Somalia (Soomaaliya)",
48093         "so",
48094         "252"
48095       ],
48096       [
48097         "South Africa",
48098         "za",
48099         "27"
48100       ],
48101       [
48102         "South Korea (대한민국)",
48103         "kr",
48104         "82"
48105       ],
48106       [
48107         "South Sudan (‫جنوب السودان‬‎)",
48108         "ss",
48109         "211"
48110       ],
48111       [
48112         "Spain (España)",
48113         "es",
48114         "34"
48115       ],
48116       [
48117         "Sri Lanka (ශ්‍රී ලංකාව)",
48118         "lk",
48119         "94"
48120       ],
48121       [
48122         "Sudan (‫السودان‬‎)",
48123         "sd",
48124         "249"
48125       ],
48126       [
48127         "Suriname",
48128         "sr",
48129         "597"
48130       ],
48131       [
48132         "Svalbard and Jan Mayen",
48133         "sj",
48134         "47",
48135         1
48136       ],
48137       [
48138         "Swaziland",
48139         "sz",
48140         "268"
48141       ],
48142       [
48143         "Sweden (Sverige)",
48144         "se",
48145         "46"
48146       ],
48147       [
48148         "Switzerland (Schweiz)",
48149         "ch",
48150         "41"
48151       ],
48152       [
48153         "Syria (‫سوريا‬‎)",
48154         "sy",
48155         "963"
48156       ],
48157       [
48158         "Taiwan (台灣)",
48159         "tw",
48160         "886"
48161       ],
48162       [
48163         "Tajikistan",
48164         "tj",
48165         "992"
48166       ],
48167       [
48168         "Tanzania",
48169         "tz",
48170         "255"
48171       ],
48172       [
48173         "Thailand (ไทย)",
48174         "th",
48175         "66"
48176       ],
48177       [
48178         "Timor-Leste",
48179         "tl",
48180         "670"
48181       ],
48182       [
48183         "Togo",
48184         "tg",
48185         "228"
48186       ],
48187       [
48188         "Tokelau",
48189         "tk",
48190         "690"
48191       ],
48192       [
48193         "Tonga",
48194         "to",
48195         "676"
48196       ],
48197       [
48198         "Trinidad and Tobago",
48199         "tt",
48200         "1868"
48201       ],
48202       [
48203         "Tunisia (‫تونس‬‎)",
48204         "tn",
48205         "216"
48206       ],
48207       [
48208         "Turkey (Türkiye)",
48209         "tr",
48210         "90"
48211       ],
48212       [
48213         "Turkmenistan",
48214         "tm",
48215         "993"
48216       ],
48217       [
48218         "Turks and Caicos Islands",
48219         "tc",
48220         "1649"
48221       ],
48222       [
48223         "Tuvalu",
48224         "tv",
48225         "688"
48226       ],
48227       [
48228         "U.S. Virgin Islands",
48229         "vi",
48230         "1340"
48231       ],
48232       [
48233         "Uganda",
48234         "ug",
48235         "256"
48236       ],
48237       [
48238         "Ukraine (Україна)",
48239         "ua",
48240         "380"
48241       ],
48242       [
48243         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
48244         "ae",
48245         "971"
48246       ],
48247       [
48248         "United Kingdom",
48249         "gb",
48250         "44",
48251         0
48252       ],
48253       [
48254         "United States",
48255         "us",
48256         "1",
48257         0
48258       ],
48259       [
48260         "Uruguay",
48261         "uy",
48262         "598"
48263       ],
48264       [
48265         "Uzbekistan (Oʻzbekiston)",
48266         "uz",
48267         "998"
48268       ],
48269       [
48270         "Vanuatu",
48271         "vu",
48272         "678"
48273       ],
48274       [
48275         "Vatican City (Città del Vaticano)",
48276         "va",
48277         "39",
48278         1
48279       ],
48280       [
48281         "Venezuela",
48282         "ve",
48283         "58"
48284       ],
48285       [
48286         "Vietnam (Việt Nam)",
48287         "vn",
48288         "84"
48289       ],
48290       [
48291         "Wallis and Futuna (Wallis-et-Futuna)",
48292         "wf",
48293         "681"
48294       ],
48295       [
48296         "Western Sahara (‫الصحراء الغربية‬‎)",
48297         "eh",
48298         "212",
48299         1
48300       ],
48301       [
48302         "Yemen (‫اليمن‬‎)",
48303         "ye",
48304         "967"
48305       ],
48306       [
48307         "Zambia",
48308         "zm",
48309         "260"
48310       ],
48311       [
48312         "Zimbabwe",
48313         "zw",
48314         "263"
48315       ],
48316       [
48317         "Åland Islands",
48318         "ax",
48319         "358",
48320         1
48321       ]
48322   ];
48323   
48324   return d;
48325 }/**
48326 *    This script refer to:
48327 *    Title: International Telephone Input
48328 *    Author: Jack O'Connor
48329 *    Code version:  v12.1.12
48330 *    Availability: https://github.com/jackocnr/intl-tel-input.git
48331 **/
48332
48333 /**
48334  * @class Roo.bootstrap.form.PhoneInput
48335  * @extends Roo.bootstrap.form.TriggerField
48336  * An input with International dial-code selection
48337  
48338  * @cfg {String} defaultDialCode default '+852'
48339  * @cfg {Array} preferedCountries default []
48340   
48341  * @constructor
48342  * Create a new PhoneInput.
48343  * @param {Object} config Configuration options
48344  */
48345
48346 Roo.bootstrap.form.PhoneInput = function(config) {
48347     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48348 };
48349
48350 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48351         /**
48352         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48353         */
48354         listWidth: undefined,
48355         
48356         selectedClass: 'active',
48357         
48358         invalidClass : "has-warning",
48359         
48360         validClass: 'has-success',
48361         
48362         allowed: '0123456789',
48363         
48364         max_length: 15,
48365         
48366         /**
48367          * @cfg {String} defaultDialCode The default dial code when initializing the input
48368          */
48369         defaultDialCode: '+852',
48370         
48371         /**
48372          * @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
48373          */
48374         preferedCountries: false,
48375         
48376         getAutoCreate : function()
48377         {
48378             var data = Roo.bootstrap.form.PhoneInputData();
48379             var align = this.labelAlign || this.parentLabelAlign();
48380             var id = Roo.id();
48381             
48382             this.allCountries = [];
48383             this.dialCodeMapping = [];
48384             
48385             for (var i = 0; i < data.length; i++) {
48386               var c = data[i];
48387               this.allCountries[i] = {
48388                 name: c[0],
48389                 iso2: c[1],
48390                 dialCode: c[2],
48391                 priority: c[3] || 0,
48392                 areaCodes: c[4] || null
48393               };
48394               this.dialCodeMapping[c[2]] = {
48395                   name: c[0],
48396                   iso2: c[1],
48397                   priority: c[3] || 0,
48398                   areaCodes: c[4] || null
48399               };
48400             }
48401             
48402             var cfg = {
48403                 cls: 'form-group',
48404                 cn: []
48405             };
48406             
48407             var input =  {
48408                 tag: 'input',
48409                 id : id,
48410                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48411                 maxlength: this.max_length,
48412                 cls : 'form-control tel-input',
48413                 autocomplete: 'new-password'
48414             };
48415             
48416             var hiddenInput = {
48417                 tag: 'input',
48418                 type: 'hidden',
48419                 cls: 'hidden-tel-input'
48420             };
48421             
48422             if (this.name) {
48423                 hiddenInput.name = this.name;
48424             }
48425             
48426             if (this.disabled) {
48427                 input.disabled = true;
48428             }
48429             
48430             var flag_container = {
48431                 tag: 'div',
48432                 cls: 'flag-box',
48433                 cn: [
48434                     {
48435                         tag: 'div',
48436                         cls: 'flag'
48437                     },
48438                     {
48439                         tag: 'div',
48440                         cls: 'caret'
48441                     }
48442                 ]
48443             };
48444             
48445             var box = {
48446                 tag: 'div',
48447                 cls: this.hasFeedback ? 'has-feedback' : '',
48448                 cn: [
48449                     hiddenInput,
48450                     input,
48451                     {
48452                         tag: 'input',
48453                         cls: 'dial-code-holder',
48454                         disabled: true
48455                     }
48456                 ]
48457             };
48458             
48459             var container = {
48460                 cls: 'roo-select2-container input-group',
48461                 cn: [
48462                     flag_container,
48463                     box
48464                 ]
48465             };
48466             
48467             if (this.fieldLabel.length) {
48468                 var indicator = {
48469                     tag: 'i',
48470                     tooltip: 'This field is required'
48471                 };
48472                 
48473                 var label = {
48474                     tag: 'label',
48475                     'for':  id,
48476                     cls: 'control-label',
48477                     cn: []
48478                 };
48479                 
48480                 var label_text = {
48481                     tag: 'span',
48482                     html: this.fieldLabel
48483                 };
48484                 
48485                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48486                 label.cn = [
48487                     indicator,
48488                     label_text
48489                 ];
48490                 
48491                 if(this.indicatorpos == 'right') {
48492                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48493                     label.cn = [
48494                         label_text,
48495                         indicator
48496                     ];
48497                 }
48498                 
48499                 if(align == 'left') {
48500                     container = {
48501                         tag: 'div',
48502                         cn: [
48503                             container
48504                         ]
48505                     };
48506                     
48507                     if(this.labelWidth > 12){
48508                         label.style = "width: " + this.labelWidth + 'px';
48509                     }
48510                     if(this.labelWidth < 13 && this.labelmd == 0){
48511                         this.labelmd = this.labelWidth;
48512                     }
48513                     if(this.labellg > 0){
48514                         label.cls += ' col-lg-' + this.labellg;
48515                         input.cls += ' col-lg-' + (12 - this.labellg);
48516                     }
48517                     if(this.labelmd > 0){
48518                         label.cls += ' col-md-' + this.labelmd;
48519                         container.cls += ' col-md-' + (12 - this.labelmd);
48520                     }
48521                     if(this.labelsm > 0){
48522                         label.cls += ' col-sm-' + this.labelsm;
48523                         container.cls += ' col-sm-' + (12 - this.labelsm);
48524                     }
48525                     if(this.labelxs > 0){
48526                         label.cls += ' col-xs-' + this.labelxs;
48527                         container.cls += ' col-xs-' + (12 - this.labelxs);
48528                     }
48529                 }
48530             }
48531             
48532             cfg.cn = [
48533                 label,
48534                 container
48535             ];
48536             
48537             var settings = this;
48538             
48539             ['xs','sm','md','lg'].map(function(size){
48540                 if (settings[size]) {
48541                     cfg.cls += ' col-' + size + '-' + settings[size];
48542                 }
48543             });
48544             
48545             this.store = new Roo.data.Store({
48546                 proxy : new Roo.data.MemoryProxy({}),
48547                 reader : new Roo.data.JsonReader({
48548                     fields : [
48549                         {
48550                             'name' : 'name',
48551                             'type' : 'string'
48552                         },
48553                         {
48554                             'name' : 'iso2',
48555                             'type' : 'string'
48556                         },
48557                         {
48558                             'name' : 'dialCode',
48559                             'type' : 'string'
48560                         },
48561                         {
48562                             'name' : 'priority',
48563                             'type' : 'string'
48564                         },
48565                         {
48566                             'name' : 'areaCodes',
48567                             'type' : 'string'
48568                         }
48569                     ]
48570                 })
48571             });
48572             
48573             if(!this.preferedCountries) {
48574                 this.preferedCountries = [
48575                     'hk',
48576                     'gb',
48577                     'us'
48578                 ];
48579             }
48580             
48581             var p = this.preferedCountries.reverse();
48582             
48583             if(p) {
48584                 for (var i = 0; i < p.length; i++) {
48585                     for (var j = 0; j < this.allCountries.length; j++) {
48586                         if(this.allCountries[j].iso2 == p[i]) {
48587                             var t = this.allCountries[j];
48588                             this.allCountries.splice(j,1);
48589                             this.allCountries.unshift(t);
48590                         }
48591                     } 
48592                 }
48593             }
48594             
48595             this.store.proxy.data = {
48596                 success: true,
48597                 data: this.allCountries
48598             };
48599             
48600             return cfg;
48601         },
48602         
48603         initEvents : function()
48604         {
48605             this.createList();
48606             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48607             
48608             this.indicator = this.indicatorEl();
48609             this.flag = this.flagEl();
48610             this.dialCodeHolder = this.dialCodeHolderEl();
48611             
48612             this.trigger = this.el.select('div.flag-box',true).first();
48613             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48614             
48615             var _this = this;
48616             
48617             (function(){
48618                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48619                 _this.list.setWidth(lw);
48620             }).defer(100);
48621             
48622             this.list.on('mouseover', this.onViewOver, this);
48623             this.list.on('mousemove', this.onViewMove, this);
48624             this.inputEl().on("keyup", this.onKeyUp, this);
48625             this.inputEl().on("keypress", this.onKeyPress, this);
48626             
48627             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48628
48629             this.view = new Roo.View(this.list, this.tpl, {
48630                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48631             });
48632             
48633             this.view.on('click', this.onViewClick, this);
48634             this.setValue(this.defaultDialCode);
48635         },
48636         
48637         onTriggerClick : function(e)
48638         {
48639             Roo.log('trigger click');
48640             if(this.disabled){
48641                 return;
48642             }
48643             
48644             if(this.isExpanded()){
48645                 this.collapse();
48646                 this.hasFocus = false;
48647             }else {
48648                 this.store.load({});
48649                 this.hasFocus = true;
48650                 this.expand();
48651             }
48652         },
48653         
48654         isExpanded : function()
48655         {
48656             return this.list.isVisible();
48657         },
48658         
48659         collapse : function()
48660         {
48661             if(!this.isExpanded()){
48662                 return;
48663             }
48664             this.list.hide();
48665             Roo.get(document).un('mousedown', this.collapseIf, this);
48666             Roo.get(document).un('mousewheel', this.collapseIf, this);
48667             this.fireEvent('collapse', this);
48668             this.validate();
48669         },
48670         
48671         expand : function()
48672         {
48673             Roo.log('expand');
48674
48675             if(this.isExpanded() || !this.hasFocus){
48676                 return;
48677             }
48678             
48679             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48680             this.list.setWidth(lw);
48681             
48682             this.list.show();
48683             this.restrictHeight();
48684             
48685             Roo.get(document).on('mousedown', this.collapseIf, this);
48686             Roo.get(document).on('mousewheel', this.collapseIf, this);
48687             
48688             this.fireEvent('expand', this);
48689         },
48690         
48691         restrictHeight : function()
48692         {
48693             this.list.alignTo(this.inputEl(), this.listAlign);
48694             this.list.alignTo(this.inputEl(), this.listAlign);
48695         },
48696         
48697         onViewOver : function(e, t)
48698         {
48699             if(this.inKeyMode){
48700                 return;
48701             }
48702             var item = this.view.findItemFromChild(t);
48703             
48704             if(item){
48705                 var index = this.view.indexOf(item);
48706                 this.select(index, false);
48707             }
48708         },
48709
48710         // private
48711         onViewClick : function(view, doFocus, el, e)
48712         {
48713             var index = this.view.getSelectedIndexes()[0];
48714             
48715             var r = this.store.getAt(index);
48716             
48717             if(r){
48718                 this.onSelect(r, index);
48719             }
48720             if(doFocus !== false && !this.blockFocus){
48721                 this.inputEl().focus();
48722             }
48723         },
48724         
48725         onViewMove : function(e, t)
48726         {
48727             this.inKeyMode = false;
48728         },
48729         
48730         select : function(index, scrollIntoView)
48731         {
48732             this.selectedIndex = index;
48733             this.view.select(index);
48734             if(scrollIntoView !== false){
48735                 var el = this.view.getNode(index);
48736                 if(el){
48737                     this.list.scrollChildIntoView(el, false);
48738                 }
48739             }
48740         },
48741         
48742         createList : function()
48743         {
48744             this.list = Roo.get(document.body).createChild({
48745                 tag: 'ul',
48746                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48747                 style: 'display:none'
48748             });
48749             
48750             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48751         },
48752         
48753         collapseIf : function(e)
48754         {
48755             var in_combo  = e.within(this.el);
48756             var in_list =  e.within(this.list);
48757             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48758             
48759             if (in_combo || in_list || is_list) {
48760                 return;
48761             }
48762             this.collapse();
48763         },
48764         
48765         onSelect : function(record, index)
48766         {
48767             if(this.fireEvent('beforeselect', this, record, index) !== false){
48768                 
48769                 this.setFlagClass(record.data.iso2);
48770                 this.setDialCode(record.data.dialCode);
48771                 this.hasFocus = false;
48772                 this.collapse();
48773                 this.fireEvent('select', this, record, index);
48774             }
48775         },
48776         
48777         flagEl : function()
48778         {
48779             var flag = this.el.select('div.flag',true).first();
48780             if(!flag){
48781                 return false;
48782             }
48783             return flag;
48784         },
48785         
48786         dialCodeHolderEl : function()
48787         {
48788             var d = this.el.select('input.dial-code-holder',true).first();
48789             if(!d){
48790                 return false;
48791             }
48792             return d;
48793         },
48794         
48795         setDialCode : function(v)
48796         {
48797             this.dialCodeHolder.dom.value = '+'+v;
48798         },
48799         
48800         setFlagClass : function(n)
48801         {
48802             this.flag.dom.className = 'flag '+n;
48803         },
48804         
48805         getValue : function()
48806         {
48807             var v = this.inputEl().getValue();
48808             if(this.dialCodeHolder) {
48809                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48810             }
48811             return v;
48812         },
48813         
48814         setValue : function(v)
48815         {
48816             var d = this.getDialCode(v);
48817             
48818             //invalid dial code
48819             if(v.length == 0 || !d || d.length == 0) {
48820                 if(this.rendered){
48821                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48822                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48823                 }
48824                 return;
48825             }
48826             
48827             //valid dial code
48828             this.setFlagClass(this.dialCodeMapping[d].iso2);
48829             this.setDialCode(d);
48830             this.inputEl().dom.value = v.replace('+'+d,'');
48831             this.hiddenEl().dom.value = this.getValue();
48832             
48833             this.validate();
48834         },
48835         
48836         getDialCode : function(v)
48837         {
48838             v = v ||  '';
48839             
48840             if (v.length == 0) {
48841                 return this.dialCodeHolder.dom.value;
48842             }
48843             
48844             var dialCode = "";
48845             if (v.charAt(0) != "+") {
48846                 return false;
48847             }
48848             var numericChars = "";
48849             for (var i = 1; i < v.length; i++) {
48850               var c = v.charAt(i);
48851               if (!isNaN(c)) {
48852                 numericChars += c;
48853                 if (this.dialCodeMapping[numericChars]) {
48854                   dialCode = v.substr(1, i);
48855                 }
48856                 if (numericChars.length == 4) {
48857                   break;
48858                 }
48859               }
48860             }
48861             return dialCode;
48862         },
48863         
48864         reset : function()
48865         {
48866             this.setValue(this.defaultDialCode);
48867             this.validate();
48868         },
48869         
48870         hiddenEl : function()
48871         {
48872             return this.el.select('input.hidden-tel-input',true).first();
48873         },
48874         
48875         // after setting val
48876         onKeyUp : function(e){
48877             this.setValue(this.getValue());
48878         },
48879         
48880         onKeyPress : function(e){
48881             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48882                 e.stopEvent();
48883             }
48884         }
48885         
48886 });
48887 /**
48888  * @class Roo.bootstrap.form.MoneyField
48889  * @extends Roo.bootstrap.form.ComboBox
48890  * Bootstrap MoneyField class
48891  * 
48892  * @constructor
48893  * Create a new MoneyField.
48894  * @param {Object} config Configuration options
48895  */
48896
48897 Roo.bootstrap.form.MoneyField = function(config) {
48898     
48899     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48900     
48901 };
48902
48903 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48904     
48905     /**
48906      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48907      */
48908     allowDecimals : true,
48909     /**
48910      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48911      */
48912     decimalSeparator : ".",
48913     /**
48914      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48915      */
48916     decimalPrecision : 0,
48917     /**
48918      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48919      */
48920     allowNegative : true,
48921     /**
48922      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48923      */
48924     allowZero: true,
48925     /**
48926      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48927      */
48928     minValue : Number.NEGATIVE_INFINITY,
48929     /**
48930      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48931      */
48932     maxValue : Number.MAX_VALUE,
48933     /**
48934      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48935      */
48936     minText : "The minimum value for this field is {0}",
48937     /**
48938      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48939      */
48940     maxText : "The maximum value for this field is {0}",
48941     /**
48942      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48943      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48944      */
48945     nanText : "{0} is not a valid number",
48946     /**
48947      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48948      */
48949     castInt : true,
48950     /**
48951      * @cfg {String} defaults currency of the MoneyField
48952      * value should be in lkey
48953      */
48954     defaultCurrency : false,
48955     /**
48956      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48957      */
48958     thousandsDelimiter : false,
48959     /**
48960      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48961      */
48962     max_length: false,
48963     
48964     inputlg : 9,
48965     inputmd : 9,
48966     inputsm : 9,
48967     inputxs : 6,
48968      /**
48969      * @cfg {Roo.data.Store} store  Store to lookup currency??
48970      */
48971     store : false,
48972     
48973     getAutoCreate : function()
48974     {
48975         var align = this.labelAlign || this.parentLabelAlign();
48976         
48977         var id = Roo.id();
48978
48979         var cfg = {
48980             cls: 'form-group',
48981             cn: []
48982         };
48983
48984         var input =  {
48985             tag: 'input',
48986             id : id,
48987             cls : 'form-control roo-money-amount-input',
48988             autocomplete: 'new-password'
48989         };
48990         
48991         var hiddenInput = {
48992             tag: 'input',
48993             type: 'hidden',
48994             id: Roo.id(),
48995             cls: 'hidden-number-input'
48996         };
48997         
48998         if(this.max_length) {
48999             input.maxlength = this.max_length; 
49000         }
49001         
49002         if (this.name) {
49003             hiddenInput.name = this.name;
49004         }
49005
49006         if (this.disabled) {
49007             input.disabled = true;
49008         }
49009
49010         var clg = 12 - this.inputlg;
49011         var cmd = 12 - this.inputmd;
49012         var csm = 12 - this.inputsm;
49013         var cxs = 12 - this.inputxs;
49014         
49015         var container = {
49016             tag : 'div',
49017             cls : 'row roo-money-field',
49018             cn : [
49019                 {
49020                     tag : 'div',
49021                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49022                     cn : [
49023                         {
49024                             tag : 'div',
49025                             cls: 'roo-select2-container input-group',
49026                             cn: [
49027                                 {
49028                                     tag : 'input',
49029                                     cls : 'form-control roo-money-currency-input',
49030                                     autocomplete: 'new-password',
49031                                     readOnly : 1,
49032                                     name : this.currencyName
49033                                 },
49034                                 {
49035                                     tag :'span',
49036                                     cls : 'input-group-addon',
49037                                     cn : [
49038                                         {
49039                                             tag: 'span',
49040                                             cls: 'caret'
49041                                         }
49042                                     ]
49043                                 }
49044                             ]
49045                         }
49046                     ]
49047                 },
49048                 {
49049                     tag : 'div',
49050                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49051                     cn : [
49052                         {
49053                             tag: 'div',
49054                             cls: this.hasFeedback ? 'has-feedback' : '',
49055                             cn: [
49056                                 input
49057                             ]
49058                         }
49059                     ]
49060                 }
49061             ]
49062             
49063         };
49064         
49065         if (this.fieldLabel.length) {
49066             var indicator = {
49067                 tag: 'i',
49068                 tooltip: 'This field is required'
49069             };
49070
49071             var label = {
49072                 tag: 'label',
49073                 'for':  id,
49074                 cls: 'control-label',
49075                 cn: []
49076             };
49077
49078             var label_text = {
49079                 tag: 'span',
49080                 html: this.fieldLabel
49081             };
49082
49083             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49084             label.cn = [
49085                 indicator,
49086                 label_text
49087             ];
49088
49089             if(this.indicatorpos == 'right') {
49090                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49091                 label.cn = [
49092                     label_text,
49093                     indicator
49094                 ];
49095             }
49096
49097             if(align == 'left') {
49098                 container = {
49099                     tag: 'div',
49100                     cn: [
49101                         container
49102                     ]
49103                 };
49104
49105                 if(this.labelWidth > 12){
49106                     label.style = "width: " + this.labelWidth + 'px';
49107                 }
49108                 if(this.labelWidth < 13 && this.labelmd == 0){
49109                     this.labelmd = this.labelWidth;
49110                 }
49111                 if(this.labellg > 0){
49112                     label.cls += ' col-lg-' + this.labellg;
49113                     input.cls += ' col-lg-' + (12 - this.labellg);
49114                 }
49115                 if(this.labelmd > 0){
49116                     label.cls += ' col-md-' + this.labelmd;
49117                     container.cls += ' col-md-' + (12 - this.labelmd);
49118                 }
49119                 if(this.labelsm > 0){
49120                     label.cls += ' col-sm-' + this.labelsm;
49121                     container.cls += ' col-sm-' + (12 - this.labelsm);
49122                 }
49123                 if(this.labelxs > 0){
49124                     label.cls += ' col-xs-' + this.labelxs;
49125                     container.cls += ' col-xs-' + (12 - this.labelxs);
49126                 }
49127             }
49128         }
49129
49130         cfg.cn = [
49131             label,
49132             container,
49133             hiddenInput
49134         ];
49135         
49136         var settings = this;
49137
49138         ['xs','sm','md','lg'].map(function(size){
49139             if (settings[size]) {
49140                 cfg.cls += ' col-' + size + '-' + settings[size];
49141             }
49142         });
49143         
49144         return cfg;
49145     },
49146     
49147     initEvents : function()
49148     {
49149         this.indicator = this.indicatorEl();
49150         
49151         this.initCurrencyEvent();
49152         
49153         this.initNumberEvent();
49154     },
49155     
49156     initCurrencyEvent : function()
49157     {
49158         if (!this.store) {
49159             throw "can not find store for combo";
49160         }
49161         
49162         this.store = Roo.factory(this.store, Roo.data);
49163         this.store.parent = this;
49164         
49165         this.createList();
49166         
49167         this.triggerEl = this.el.select('.input-group-addon', true).first();
49168         
49169         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49170         
49171         var _this = this;
49172         
49173         (function(){
49174             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49175             _this.list.setWidth(lw);
49176         }).defer(100);
49177         
49178         this.list.on('mouseover', this.onViewOver, this);
49179         this.list.on('mousemove', this.onViewMove, this);
49180         this.list.on('scroll', this.onViewScroll, this);
49181         
49182         if(!this.tpl){
49183             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49184         }
49185         
49186         this.view = new Roo.View(this.list, this.tpl, {
49187             singleSelect:true, store: this.store, selectedClass: this.selectedClass
49188         });
49189         
49190         this.view.on('click', this.onViewClick, this);
49191         
49192         this.store.on('beforeload', this.onBeforeLoad, this);
49193         this.store.on('load', this.onLoad, this);
49194         this.store.on('loadexception', this.onLoadException, this);
49195         
49196         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49197             "up" : function(e){
49198                 this.inKeyMode = true;
49199                 this.selectPrev();
49200             },
49201
49202             "down" : function(e){
49203                 if(!this.isExpanded()){
49204                     this.onTriggerClick();
49205                 }else{
49206                     this.inKeyMode = true;
49207                     this.selectNext();
49208                 }
49209             },
49210
49211             "enter" : function(e){
49212                 this.collapse();
49213                 
49214                 if(this.fireEvent("specialkey", this, e)){
49215                     this.onViewClick(false);
49216                 }
49217                 
49218                 return true;
49219             },
49220
49221             "esc" : function(e){
49222                 this.collapse();
49223             },
49224
49225             "tab" : function(e){
49226                 this.collapse();
49227                 
49228                 if(this.fireEvent("specialkey", this, e)){
49229                     this.onViewClick(false);
49230                 }
49231                 
49232                 return true;
49233             },
49234
49235             scope : this,
49236
49237             doRelay : function(foo, bar, hname){
49238                 if(hname == 'down' || this.scope.isExpanded()){
49239                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49240                 }
49241                 return true;
49242             },
49243
49244             forceKeyDown: true
49245         });
49246         
49247         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49248         
49249     },
49250     
49251     initNumberEvent : function(e)
49252     {
49253         this.inputEl().on("keydown" , this.fireKey,  this);
49254         this.inputEl().on("focus", this.onFocus,  this);
49255         this.inputEl().on("blur", this.onBlur,  this);
49256         
49257         this.inputEl().relayEvent('keyup', this);
49258         
49259         if(this.indicator){
49260             this.indicator.addClass('invisible');
49261         }
49262  
49263         this.originalValue = this.getValue();
49264         
49265         if(this.validationEvent == 'keyup'){
49266             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49267             this.inputEl().on('keyup', this.filterValidation, this);
49268         }
49269         else if(this.validationEvent !== false){
49270             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49271         }
49272         
49273         if(this.selectOnFocus){
49274             this.on("focus", this.preFocus, this);
49275             
49276         }
49277         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49278             this.inputEl().on("keypress", this.filterKeys, this);
49279         } else {
49280             this.inputEl().relayEvent('keypress', this);
49281         }
49282         
49283         var allowed = "0123456789";
49284         
49285         if(this.allowDecimals){
49286             allowed += this.decimalSeparator;
49287         }
49288         
49289         if(this.allowNegative){
49290             allowed += "-";
49291         }
49292         
49293         if(this.thousandsDelimiter) {
49294             allowed += ",";
49295         }
49296         
49297         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49298         
49299         var keyPress = function(e){
49300             
49301             var k = e.getKey();
49302             
49303             var c = e.getCharCode();
49304             
49305             if(
49306                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49307                     allowed.indexOf(String.fromCharCode(c)) === -1
49308             ){
49309                 e.stopEvent();
49310                 return;
49311             }
49312             
49313             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49314                 return;
49315             }
49316             
49317             if(allowed.indexOf(String.fromCharCode(c)) === -1){
49318                 e.stopEvent();
49319             }
49320         };
49321         
49322         this.inputEl().on("keypress", keyPress, this);
49323         
49324     },
49325     
49326     onTriggerClick : function(e)
49327     {   
49328         if(this.disabled){
49329             return;
49330         }
49331         
49332         this.page = 0;
49333         this.loadNext = false;
49334         
49335         if(this.isExpanded()){
49336             this.collapse();
49337             return;
49338         }
49339         
49340         this.hasFocus = true;
49341         
49342         if(this.triggerAction == 'all') {
49343             this.doQuery(this.allQuery, true);
49344             return;
49345         }
49346         
49347         this.doQuery(this.getRawValue());
49348     },
49349     
49350     getCurrency : function()
49351     {   
49352         var v = this.currencyEl().getValue();
49353         
49354         return v;
49355     },
49356     
49357     restrictHeight : function()
49358     {
49359         this.list.alignTo(this.currencyEl(), this.listAlign);
49360         this.list.alignTo(this.currencyEl(), this.listAlign);
49361     },
49362     
49363     onViewClick : function(view, doFocus, el, e)
49364     {
49365         var index = this.view.getSelectedIndexes()[0];
49366         
49367         var r = this.store.getAt(index);
49368         
49369         if(r){
49370             this.onSelect(r, index);
49371         }
49372     },
49373     
49374     onSelect : function(record, index){
49375         
49376         if(this.fireEvent('beforeselect', this, record, index) !== false){
49377         
49378             this.setFromCurrencyData(index > -1 ? record.data : false);
49379             
49380             this.collapse();
49381             
49382             this.fireEvent('select', this, record, index);
49383         }
49384     },
49385     
49386     setFromCurrencyData : function(o)
49387     {
49388         var currency = '';
49389         
49390         this.lastCurrency = o;
49391         
49392         if (this.currencyField) {
49393             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49394         } else {
49395             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49396         }
49397         
49398         this.lastSelectionText = currency;
49399         
49400         //setting default currency
49401         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49402             this.setCurrency(this.defaultCurrency);
49403             return;
49404         }
49405         
49406         this.setCurrency(currency);
49407     },
49408     
49409     setFromData : function(o)
49410     {
49411         var c = {};
49412         
49413         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49414         
49415         this.setFromCurrencyData(c);
49416         
49417         var value = '';
49418         
49419         if (this.name) {
49420             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49421         } else {
49422             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49423         }
49424         
49425         this.setValue(value);
49426         
49427     },
49428     
49429     setCurrency : function(v)
49430     {   
49431         this.currencyValue = v;
49432         
49433         if(this.rendered){
49434             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49435             this.validate();
49436         }
49437     },
49438     
49439     setValue : function(v)
49440     {
49441         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49442         
49443         this.value = v;
49444         
49445         if(this.rendered){
49446             
49447             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49448             
49449             this.inputEl().dom.value = (v == '') ? '' :
49450                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49451             
49452             if(!this.allowZero && v === '0') {
49453                 this.hiddenEl().dom.value = '';
49454                 this.inputEl().dom.value = '';
49455             }
49456             
49457             this.validate();
49458         }
49459     },
49460     
49461     getRawValue : function()
49462     {
49463         var v = this.inputEl().getValue();
49464         
49465         return v;
49466     },
49467     
49468     getValue : function()
49469     {
49470         return this.fixPrecision(this.parseValue(this.getRawValue()));
49471     },
49472     
49473     parseValue : function(value)
49474     {
49475         if(this.thousandsDelimiter) {
49476             value += "";
49477             r = new RegExp(",", "g");
49478             value = value.replace(r, "");
49479         }
49480         
49481         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49482         return isNaN(value) ? '' : value;
49483         
49484     },
49485     
49486     fixPrecision : function(value)
49487     {
49488         if(this.thousandsDelimiter) {
49489             value += "";
49490             r = new RegExp(",", "g");
49491             value = value.replace(r, "");
49492         }
49493         
49494         var nan = isNaN(value);
49495         
49496         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49497             return nan ? '' : value;
49498         }
49499         return parseFloat(value).toFixed(this.decimalPrecision);
49500     },
49501     
49502     decimalPrecisionFcn : function(v)
49503     {
49504         return Math.floor(v);
49505     },
49506     
49507     validateValue : function(value)
49508     {
49509         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49510             return false;
49511         }
49512         
49513         var num = this.parseValue(value);
49514         
49515         if(isNaN(num)){
49516             this.markInvalid(String.format(this.nanText, value));
49517             return false;
49518         }
49519         
49520         if(num < this.minValue){
49521             this.markInvalid(String.format(this.minText, this.minValue));
49522             return false;
49523         }
49524         
49525         if(num > this.maxValue){
49526             this.markInvalid(String.format(this.maxText, this.maxValue));
49527             return false;
49528         }
49529         
49530         return true;
49531     },
49532     
49533     validate : function()
49534     {
49535         if(this.disabled || this.allowBlank){
49536             this.markValid();
49537             return true;
49538         }
49539         
49540         var currency = this.getCurrency();
49541         
49542         if(this.validateValue(this.getRawValue()) && currency.length){
49543             this.markValid();
49544             return true;
49545         }
49546         
49547         this.markInvalid();
49548         return false;
49549     },
49550     
49551     getName: function()
49552     {
49553         return this.name;
49554     },
49555     
49556     beforeBlur : function()
49557     {
49558         if(!this.castInt){
49559             return;
49560         }
49561         
49562         var v = this.parseValue(this.getRawValue());
49563         
49564         if(v || v == 0){
49565             this.setValue(v);
49566         }
49567     },
49568     
49569     onBlur : function()
49570     {
49571         this.beforeBlur();
49572         
49573         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49574             //this.el.removeClass(this.focusClass);
49575         }
49576         
49577         this.hasFocus = false;
49578         
49579         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49580             this.validate();
49581         }
49582         
49583         var v = this.getValue();
49584         
49585         if(String(v) !== String(this.startValue)){
49586             this.fireEvent('change', this, v, this.startValue);
49587         }
49588         
49589         this.fireEvent("blur", this);
49590     },
49591     
49592     inputEl : function()
49593     {
49594         return this.el.select('.roo-money-amount-input', true).first();
49595     },
49596     
49597     currencyEl : function()
49598     {
49599         return this.el.select('.roo-money-currency-input', true).first();
49600     },
49601     
49602     hiddenEl : function()
49603     {
49604         return this.el.select('input.hidden-number-input',true).first();
49605     }
49606     
49607 });/**
49608  * @class Roo.bootstrap.BezierSignature
49609  * @extends Roo.bootstrap.Component
49610  * Bootstrap BezierSignature class
49611  * This script refer to:
49612  *    Title: Signature Pad
49613  *    Author: szimek
49614  *    Availability: https://github.com/szimek/signature_pad
49615  *
49616  * @constructor
49617  * Create a new BezierSignature
49618  * @param {Object} config The config object
49619  */
49620
49621 Roo.bootstrap.BezierSignature = function(config){
49622     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49623     this.addEvents({
49624         "resize" : true
49625     });
49626 };
49627
49628 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49629 {
49630      
49631     curve_data: [],
49632     
49633     is_empty: true,
49634     
49635     mouse_btn_down: true,
49636     
49637     /**
49638      * @cfg {int} canvas height
49639      */
49640     canvas_height: '200px',
49641     
49642     /**
49643      * @cfg {float|function} Radius of a single dot.
49644      */ 
49645     dot_size: false,
49646     
49647     /**
49648      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49649      */
49650     min_width: 0.5,
49651     
49652     /**
49653      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49654      */
49655     max_width: 2.5,
49656     
49657     /**
49658      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49659      */
49660     throttle: 16,
49661     
49662     /**
49663      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49664      */
49665     min_distance: 5,
49666     
49667     /**
49668      * @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.
49669      */
49670     bg_color: 'rgba(0, 0, 0, 0)',
49671     
49672     /**
49673      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49674      */
49675     dot_color: 'black',
49676     
49677     /**
49678      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49679      */ 
49680     velocity_filter_weight: 0.7,
49681     
49682     /**
49683      * @cfg {function} Callback when stroke begin. 
49684      */
49685     onBegin: false,
49686     
49687     /**
49688      * @cfg {function} Callback when stroke end.
49689      */
49690     onEnd: false,
49691     
49692     getAutoCreate : function()
49693     {
49694         var cls = 'roo-signature column';
49695         
49696         if(this.cls){
49697             cls += ' ' + this.cls;
49698         }
49699         
49700         var col_sizes = [
49701             'lg',
49702             'md',
49703             'sm',
49704             'xs'
49705         ];
49706         
49707         for(var i = 0; i < col_sizes.length; i++) {
49708             if(this[col_sizes[i]]) {
49709                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49710             }
49711         }
49712         
49713         var cfg = {
49714             tag: 'div',
49715             cls: cls,
49716             cn: [
49717                 {
49718                     tag: 'div',
49719                     cls: 'roo-signature-body',
49720                     cn: [
49721                         {
49722                             tag: 'canvas',
49723                             cls: 'roo-signature-body-canvas',
49724                             height: this.canvas_height,
49725                             width: this.canvas_width
49726                         }
49727                     ]
49728                 },
49729                 {
49730                     tag: 'input',
49731                     type: 'file',
49732                     style: 'display: none'
49733                 }
49734             ]
49735         };
49736         
49737         return cfg;
49738     },
49739     
49740     initEvents: function() 
49741     {
49742         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49743         
49744         var canvas = this.canvasEl();
49745         
49746         // mouse && touch event swapping...
49747         canvas.dom.style.touchAction = 'none';
49748         canvas.dom.style.msTouchAction = 'none';
49749         
49750         this.mouse_btn_down = false;
49751         canvas.on('mousedown', this._handleMouseDown, this);
49752         canvas.on('mousemove', this._handleMouseMove, this);
49753         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49754         
49755         if (window.PointerEvent) {
49756             canvas.on('pointerdown', this._handleMouseDown, this);
49757             canvas.on('pointermove', this._handleMouseMove, this);
49758             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49759         }
49760         
49761         if ('ontouchstart' in window) {
49762             canvas.on('touchstart', this._handleTouchStart, this);
49763             canvas.on('touchmove', this._handleTouchMove, this);
49764             canvas.on('touchend', this._handleTouchEnd, this);
49765         }
49766         
49767         Roo.EventManager.onWindowResize(this.resize, this, true);
49768         
49769         // file input event
49770         this.fileEl().on('change', this.uploadImage, this);
49771         
49772         this.clear();
49773         
49774         this.resize();
49775     },
49776     
49777     resize: function(){
49778         
49779         var canvas = this.canvasEl().dom;
49780         var ctx = this.canvasElCtx();
49781         var img_data = false;
49782         
49783         if(canvas.width > 0) {
49784             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49785         }
49786         // setting canvas width will clean img data
49787         canvas.width = 0;
49788         
49789         var style = window.getComputedStyle ? 
49790             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49791             
49792         var padding_left = parseInt(style.paddingLeft) || 0;
49793         var padding_right = parseInt(style.paddingRight) || 0;
49794         
49795         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49796         
49797         if(img_data) {
49798             ctx.putImageData(img_data, 0, 0);
49799         }
49800     },
49801     
49802     _handleMouseDown: function(e)
49803     {
49804         if (e.browserEvent.which === 1) {
49805             this.mouse_btn_down = true;
49806             this.strokeBegin(e);
49807         }
49808     },
49809     
49810     _handleMouseMove: function (e)
49811     {
49812         if (this.mouse_btn_down) {
49813             this.strokeMoveUpdate(e);
49814         }
49815     },
49816     
49817     _handleMouseUp: function (e)
49818     {
49819         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49820             this.mouse_btn_down = false;
49821             this.strokeEnd(e);
49822         }
49823     },
49824     
49825     _handleTouchStart: function (e) {
49826         
49827         e.preventDefault();
49828         if (e.browserEvent.targetTouches.length === 1) {
49829             // var touch = e.browserEvent.changedTouches[0];
49830             // this.strokeBegin(touch);
49831             
49832              this.strokeBegin(e); // assume e catching the correct xy...
49833         }
49834     },
49835     
49836     _handleTouchMove: function (e) {
49837         e.preventDefault();
49838         // var touch = event.targetTouches[0];
49839         // _this._strokeMoveUpdate(touch);
49840         this.strokeMoveUpdate(e);
49841     },
49842     
49843     _handleTouchEnd: function (e) {
49844         var wasCanvasTouched = e.target === this.canvasEl().dom;
49845         if (wasCanvasTouched) {
49846             e.preventDefault();
49847             // var touch = event.changedTouches[0];
49848             // _this._strokeEnd(touch);
49849             this.strokeEnd(e);
49850         }
49851     },
49852     
49853     reset: function () {
49854         this._lastPoints = [];
49855         this._lastVelocity = 0;
49856         this._lastWidth = (this.min_width + this.max_width) / 2;
49857         this.canvasElCtx().fillStyle = this.dot_color;
49858     },
49859     
49860     strokeMoveUpdate: function(e)
49861     {
49862         this.strokeUpdate(e);
49863         
49864         if (this.throttle) {
49865             this.throttleStroke(this.strokeUpdate, this.throttle);
49866         }
49867         else {
49868             this.strokeUpdate(e);
49869         }
49870     },
49871     
49872     strokeBegin: function(e)
49873     {
49874         var newPointGroup = {
49875             color: this.dot_color,
49876             points: []
49877         };
49878         
49879         if (typeof this.onBegin === 'function') {
49880             this.onBegin(e);
49881         }
49882         
49883         this.curve_data.push(newPointGroup);
49884         this.reset();
49885         this.strokeUpdate(e);
49886     },
49887     
49888     strokeUpdate: function(e)
49889     {
49890         var rect = this.canvasEl().dom.getBoundingClientRect();
49891         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49892         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49893         var lastPoints = lastPointGroup.points;
49894         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49895         var isLastPointTooClose = lastPoint
49896             ? point.distanceTo(lastPoint) <= this.min_distance
49897             : false;
49898         var color = lastPointGroup.color;
49899         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49900             var curve = this.addPoint(point);
49901             if (!lastPoint) {
49902                 this.drawDot({color: color, point: point});
49903             }
49904             else if (curve) {
49905                 this.drawCurve({color: color, curve: curve});
49906             }
49907             lastPoints.push({
49908                 time: point.time,
49909                 x: point.x,
49910                 y: point.y
49911             });
49912         }
49913     },
49914     
49915     strokeEnd: function(e)
49916     {
49917         this.strokeUpdate(e);
49918         if (typeof this.onEnd === 'function') {
49919             this.onEnd(e);
49920         }
49921     },
49922     
49923     addPoint:  function (point) {
49924         var _lastPoints = this._lastPoints;
49925         _lastPoints.push(point);
49926         if (_lastPoints.length > 2) {
49927             if (_lastPoints.length === 3) {
49928                 _lastPoints.unshift(_lastPoints[0]);
49929             }
49930             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49931             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49932             _lastPoints.shift();
49933             return curve;
49934         }
49935         return null;
49936     },
49937     
49938     calculateCurveWidths: function (startPoint, endPoint) {
49939         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49940             (1 - this.velocity_filter_weight) * this._lastVelocity;
49941
49942         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49943         var widths = {
49944             end: newWidth,
49945             start: this._lastWidth
49946         };
49947         
49948         this._lastVelocity = velocity;
49949         this._lastWidth = newWidth;
49950         return widths;
49951     },
49952     
49953     drawDot: function (_a) {
49954         var color = _a.color, point = _a.point;
49955         var ctx = this.canvasElCtx();
49956         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49957         ctx.beginPath();
49958         this.drawCurveSegment(point.x, point.y, width);
49959         ctx.closePath();
49960         ctx.fillStyle = color;
49961         ctx.fill();
49962     },
49963     
49964     drawCurve: function (_a) {
49965         var color = _a.color, curve = _a.curve;
49966         var ctx = this.canvasElCtx();
49967         var widthDelta = curve.endWidth - curve.startWidth;
49968         var drawSteps = Math.floor(curve.length()) * 2;
49969         ctx.beginPath();
49970         ctx.fillStyle = color;
49971         for (var i = 0; i < drawSteps; i += 1) {
49972         var t = i / drawSteps;
49973         var tt = t * t;
49974         var ttt = tt * t;
49975         var u = 1 - t;
49976         var uu = u * u;
49977         var uuu = uu * u;
49978         var x = uuu * curve.startPoint.x;
49979         x += 3 * uu * t * curve.control1.x;
49980         x += 3 * u * tt * curve.control2.x;
49981         x += ttt * curve.endPoint.x;
49982         var y = uuu * curve.startPoint.y;
49983         y += 3 * uu * t * curve.control1.y;
49984         y += 3 * u * tt * curve.control2.y;
49985         y += ttt * curve.endPoint.y;
49986         var width = curve.startWidth + ttt * widthDelta;
49987         this.drawCurveSegment(x, y, width);
49988         }
49989         ctx.closePath();
49990         ctx.fill();
49991     },
49992     
49993     drawCurveSegment: function (x, y, width) {
49994         var ctx = this.canvasElCtx();
49995         ctx.moveTo(x, y);
49996         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49997         this.is_empty = false;
49998     },
49999     
50000     clear: function()
50001     {
50002         var ctx = this.canvasElCtx();
50003         var canvas = this.canvasEl().dom;
50004         ctx.fillStyle = this.bg_color;
50005         ctx.clearRect(0, 0, canvas.width, canvas.height);
50006         ctx.fillRect(0, 0, canvas.width, canvas.height);
50007         this.curve_data = [];
50008         this.reset();
50009         this.is_empty = true;
50010     },
50011     
50012     fileEl: function()
50013     {
50014         return  this.el.select('input',true).first();
50015     },
50016     
50017     canvasEl: function()
50018     {
50019         return this.el.select('canvas',true).first();
50020     },
50021     
50022     canvasElCtx: function()
50023     {
50024         return this.el.select('canvas',true).first().dom.getContext('2d');
50025     },
50026     
50027     getImage: function(type)
50028     {
50029         if(this.is_empty) {
50030             return false;
50031         }
50032         
50033         // encryption ?
50034         return this.canvasEl().dom.toDataURL('image/'+type, 1);
50035     },
50036     
50037     drawFromImage: function(img_src)
50038     {
50039         var img = new Image();
50040         
50041         img.onload = function(){
50042             this.canvasElCtx().drawImage(img, 0, 0);
50043         }.bind(this);
50044         
50045         img.src = img_src;
50046         
50047         this.is_empty = false;
50048     },
50049     
50050     selectImage: function()
50051     {
50052         this.fileEl().dom.click();
50053     },
50054     
50055     uploadImage: function(e)
50056     {
50057         var reader = new FileReader();
50058         
50059         reader.onload = function(e){
50060             var img = new Image();
50061             img.onload = function(){
50062                 this.reset();
50063                 this.canvasElCtx().drawImage(img, 0, 0);
50064             }.bind(this);
50065             img.src = e.target.result;
50066         }.bind(this);
50067         
50068         reader.readAsDataURL(e.target.files[0]);
50069     },
50070     
50071     // Bezier Point Constructor
50072     Point: (function () {
50073         function Point(x, y, time) {
50074             this.x = x;
50075             this.y = y;
50076             this.time = time || Date.now();
50077         }
50078         Point.prototype.distanceTo = function (start) {
50079             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50080         };
50081         Point.prototype.equals = function (other) {
50082             return this.x === other.x && this.y === other.y && this.time === other.time;
50083         };
50084         Point.prototype.velocityFrom = function (start) {
50085             return this.time !== start.time
50086             ? this.distanceTo(start) / (this.time - start.time)
50087             : 0;
50088         };
50089         return Point;
50090     }()),
50091     
50092     
50093     // Bezier Constructor
50094     Bezier: (function () {
50095         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50096             this.startPoint = startPoint;
50097             this.control2 = control2;
50098             this.control1 = control1;
50099             this.endPoint = endPoint;
50100             this.startWidth = startWidth;
50101             this.endWidth = endWidth;
50102         }
50103         Bezier.fromPoints = function (points, widths, scope) {
50104             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50105             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50106             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50107         };
50108         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50109             var dx1 = s1.x - s2.x;
50110             var dy1 = s1.y - s2.y;
50111             var dx2 = s2.x - s3.x;
50112             var dy2 = s2.y - s3.y;
50113             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50114             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50115             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50116             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50117             var dxm = m1.x - m2.x;
50118             var dym = m1.y - m2.y;
50119             var k = l2 / (l1 + l2);
50120             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50121             var tx = s2.x - cm.x;
50122             var ty = s2.y - cm.y;
50123             return {
50124                 c1: new scope.Point(m1.x + tx, m1.y + ty),
50125                 c2: new scope.Point(m2.x + tx, m2.y + ty)
50126             };
50127         };
50128         Bezier.prototype.length = function () {
50129             var steps = 10;
50130             var length = 0;
50131             var px;
50132             var py;
50133             for (var i = 0; i <= steps; i += 1) {
50134                 var t = i / steps;
50135                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50136                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50137                 if (i > 0) {
50138                     var xdiff = cx - px;
50139                     var ydiff = cy - py;
50140                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50141                 }
50142                 px = cx;
50143                 py = cy;
50144             }
50145             return length;
50146         };
50147         Bezier.prototype.point = function (t, start, c1, c2, end) {
50148             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50149             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50150             + (3.0 * c2 * (1.0 - t) * t * t)
50151             + (end * t * t * t);
50152         };
50153         return Bezier;
50154     }()),
50155     
50156     throttleStroke: function(fn, wait) {
50157       if (wait === void 0) { wait = 250; }
50158       var previous = 0;
50159       var timeout = null;
50160       var result;
50161       var storedContext;
50162       var storedArgs;
50163       var later = function () {
50164           previous = Date.now();
50165           timeout = null;
50166           result = fn.apply(storedContext, storedArgs);
50167           if (!timeout) {
50168               storedContext = null;
50169               storedArgs = [];
50170           }
50171       };
50172       return function wrapper() {
50173           var args = [];
50174           for (var _i = 0; _i < arguments.length; _i++) {
50175               args[_i] = arguments[_i];
50176           }
50177           var now = Date.now();
50178           var remaining = wait - (now - previous);
50179           storedContext = this;
50180           storedArgs = args;
50181           if (remaining <= 0 || remaining > wait) {
50182               if (timeout) {
50183                   clearTimeout(timeout);
50184                   timeout = null;
50185               }
50186               previous = now;
50187               result = fn.apply(storedContext, storedArgs);
50188               if (!timeout) {
50189                   storedContext = null;
50190                   storedArgs = [];
50191               }
50192           }
50193           else if (!timeout) {
50194               timeout = window.setTimeout(later, remaining);
50195           }
50196           return result;
50197       };
50198   }
50199   
50200 });
50201
50202  
50203
50204  // old names for form elements
50205 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
50206 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
50207 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
50208 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
50209 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
50210 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
50211 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
50212 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
50213 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
50214 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
50215 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
50216 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
50217 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
50218 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
50219 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
50220 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
50221 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
50222 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
50223 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
50224 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
50225 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
50226 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
50227 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
50228 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
50229 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
50230 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
50231
50232 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
50233 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50234
50235 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
50236 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
50237
50238 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
50239 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50240 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
50241 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
50242